Restructure to monorepo (#6907)

* wip: restructure to monorepo

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* refactor create-release-pr to a package

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* build fixes

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* github workflow fixes

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix typo

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* add webpack-env types to core

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix github workflows

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* refactor/fix integration tests

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* lint fix

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* yarn run dev

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* eslint settings for vscode

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* move templates to right package

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* open-lens build fixes

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* integration test fix

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix nx task dependencies

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* use bash shell for unit tests in test workflow

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix test:unit for windows

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix win-ca webpack error in open-lens

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix win-ca webpack error in open-lens

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix build:app on windows

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* remove ELECTRON_BUILDER_EXTRA_ARGS

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* sync src/ from master

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* remove Makefile from core

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen authored and GitHub committed 2023-01-24 10:46:26 -08:00
1 parent 0066ca4ea2
commit 2657df2293
3321 files changed
+28699 -11447

No files matched your search

@@ -0,0 +1,77 @@
/**
* 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, Tray } from "electron";
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import { convertToElectronMenuTemplate } from "../reactive-tray-menu-items/converters";
import trayIconInjectable from "../menu-icon/tray-icon.injectable";
import applicationDescriptionInjectable from "../../../common/vars/application-description.injectable";
const TRAY_LOG_PREFIX = "[TRAY]";
export interface MinimalTrayMenuItem {
id: string;
parentId: string | null;
enabled: boolean;
label?: string;
click?: () => Promise<void> | void;
tooltip?: string;
separator?: boolean;
}
export interface ElectronTray {
start(): void;
stop(): void;
setMenuItems(menuItems: MinimalTrayMenuItem[]): void;
setIconPath(iconPath: string): void;
}
const electronTrayInjectable = getInjectable({
id: "electron-tray",
instantiate: (di): ElectronTray => {
const applicationDescription = di.inject(applicationDescriptionInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const isWindows = di.inject(isWindowsInjectable);
const logger = di.inject(loggerInjectable);
const trayIcon = di.inject(trayIconInjectable);
let tray: Tray;
return {
start: () => {
tray = new Tray(trayIcon.get().iconPath);
tray.setToolTip(applicationDescription);
tray.setIgnoreDoubleClickEvents(true);
if (isWindows) {
tray.on("click", () => {
showApplicationWindow()
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
});
}
},
stop: () => {
tray.destroy();
},
setMenuItems: (menuItems) => {
const template = convertToElectronMenuTemplate(menuItems);
const menu = Menu.buildFromTemplate(template);
tray.setContextMenu(menu);
},
setIconPath: (iconPath) => {
tray.setImage(iconPath);
},
};
},
causesSideEffects: true,
});
export default electronTrayInjectable;
@@ -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 { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
import electronTrayInjectable from "./electron-tray.injectable";
const startTrayInjectable = getInjectable({
id: "start-tray",
instantiate: (di) => {
const electronTray = di.inject(electronTrayInjectable);
return {
id: "start-tray",
run: () => {
electronTray.start();
},
};
},
injectionToken: onLoadOfApplicationInjectionToken,
});
export default startTrayInjectable;
@@ -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 electronTrayInjectable from "./electron-tray.injectable";
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
import stopReactiveTrayMenuItemsInjectable from "../reactive-tray-menu-items/stop-reactive-tray-menu-items.injectable";
const stopTrayInjectable = getInjectable({
id: "stop-tray",
instantiate: (di) => {
const electronTray = di.inject(electronTrayInjectable);
return {
id: "stop-tray",
run: () => void electronTray.stop(),
runAfter: di.inject(stopReactiveTrayMenuItemsInjectable),
};
},
injectionToken: beforeQuitOfBackEndInjectionToken,
});
export default stopTrayInjectable;
@@ -0,0 +1,34 @@
/**
* 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 staticFilesDirectoryInjectable from "../../../common/vars/static-files-directory.injectable";
import isMacInjectable from "../../../common/vars/is-mac.injectable";
import { camelCase, flow, upperFirst } from "lodash/fp";
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
const upperCamelCase = flow(camelCase, upperFirst);
const getTrayIconPathInjectable = getInjectable({
id: "get-tray-icon-path",
instantiate: (di) => {
const joinPaths = di.inject(joinPathsInjectable);
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
const isMac = di.inject(isMacInjectable);
const baseIconDirectory = joinPaths(
staticFilesDirectory,
"build/tray",
);
const fileSuffix = isMac ? "Template.png" : ".png";
return (name: string) => joinPaths(
baseIconDirectory,
`trayIcon${upperCamelCase(name)}${fileSuffix}`,
);
},
});
export default getTrayIconPathInjectable;
@@ -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 { computed } from "mobx";
import { trayIconInjectionToken } from "./tray-icon-injection-token";
import getTrayIconPathInjectable from "./get-tray-icon-path.injectable";
const normalTrayIconInjectable = getInjectable({
id: "normal-icon",
instantiate: (di) => {
const getTrayIconPath = di.inject(getTrayIconPathInjectable);
return {
iconPath: getTrayIconPath(""),
priority: 999,
shouldBeShown: computed(() => true),
};
},
injectionToken: trayIconInjectionToken,
});
export default normalTrayIconInjectable;
@@ -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 { reaction } from "mobx";
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
import trayIconInjectable from "./tray-icon.injectable";
const reactiveTrayMenuIconInjectable = getInjectable({
id: "reactive-tray-menu-icon",
instantiate: (di) => {
const trayMenuIcon = di.inject(trayIconInjectable);
const electronTray = di.inject(electronTrayInjectable);
return getStartableStoppable("reactive-tray-menu-icon", () => (
reaction(
() => trayMenuIcon.get(),
icon => {
electronTray.setIconPath(icon.iconPath);
},
{
fireImmediately: true,
},
)
));
},
});
export default reactiveTrayMenuIconInjectable;
@@ -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 { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
import startTrayInjectable from "../electron-tray/start-tray.injectable";
import reactiveTrayMenuIconInjectable from "./reactive.injectable";
const startReactiveTrayMenuIconInjectable = getInjectable({
id: "start-reactive-tray-menu-icon",
instantiate: (di) => {
const reactiveTrayMenuIcon = di.inject(reactiveTrayMenuIconInjectable);
return {
id: "start-reactive-tray-menu-icon",
run: () => {
reactiveTrayMenuIcon.start();
},
runAfter: di.inject(startTrayInjectable),
};
},
injectionToken: onLoadOfApplicationInjectionToken,
});
export default startReactiveTrayMenuIconInjectable;
@@ -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 { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
import reactiveTrayMenuIconInjectable from "./reactive.injectable";
const stopReactiveTrayMenuIconInjectable = getInjectable({
id: "stop-reactive-tray-menu-icon",
instantiate: (di) => {
const reactiveTrayMenuIcon = di.inject(reactiveTrayMenuIconInjectable);
return {
id: "stop-reactive-tray-menu-icon",
run: () => void reactiveTrayMenuIcon.stop(),
};
},
injectionToken: beforeQuitOfBackEndInjectionToken,
});
export default stopReactiveTrayMenuIconInjectable;
@@ -0,0 +1,16 @@
/**
* 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";
import type { IComputedValue } from "mobx";
export interface TrayIcon {
iconPath: string;
priority: number;
shouldBeShown: IComputedValue<boolean>;
}
export const trayIconInjectionToken = getInjectionToken<TrayIcon>({
id: "tray-icon-token",
});
@@ -0,0 +1,33 @@
/**
* 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 { find, sortBy } from "lodash/fp";
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { trayIconInjectionToken } from "./tray-icon-injection-token";
const trayIconInjectable = getInjectable({
id: "tray-icon",
instantiate: (di) => {
const availableIcons = di.injectMany(trayIconInjectionToken);
return computed(() => {
const mostPrioritizedIcon = pipeline(
availableIcons,
sortBy((icon) => icon.priority),
find((icon) => icon.shouldBeShown.get()),
);
if (!mostPrioritizedIcon) {
throw new Error("There should always be tray icon which is shown.");
}
return mostPrioritizedIcon;
});
},
});
export default trayIconInjectable;
@@ -0,0 +1,40 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { MinimalTrayMenuItem } from "../electron-tray/electron-tray.injectable";
export function convertToElectronMenuTemplate(trayMenuItems: MinimalTrayMenuItem[]): Electron.MenuItemConstructorOptions[] {
const toTrayMenuOptions = (parentId: string | null) => (
trayMenuItems
.filter((item) => item.parentId === parentId)
.map((trayMenuItem): Electron.MenuItemConstructorOptions => {
if (trayMenuItem.separator) {
return { id: trayMenuItem.id, type: "separator" };
}
const childItems = toTrayMenuOptions(trayMenuItem.id);
return {
id: trayMenuItem.id,
label: trayMenuItem.label,
enabled: trayMenuItem.enabled,
toolTip: trayMenuItem.tooltip,
...(childItems.length === 0
? {
type: "normal",
submenu: toTrayMenuOptions(trayMenuItem.id),
click: trayMenuItem.click,
}
: {
type: "submenu",
submenu: toTrayMenuOptions(trayMenuItem.id),
}),
};
})
);
return toTrayMenuOptions(null);
}
@@ -0,0 +1,42 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
import { reaction } from "mobx";
import type { MinimalTrayMenuItem } from "../electron-tray/electron-tray.injectable";
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
import trayMenuItemsInjectable from "../tray-menu-item/tray-menu-items.injectable";
import type { TrayMenuItem } from "../tray-menu-item/tray-menu-item-injection-token";
const reactiveTrayMenuItemsInjectable = getInjectable({
id: "reactive-tray-menu-items",
instantiate: (di) => {
const electronTray = di.inject(electronTrayInjectable);
const reactiveItems = di.inject(trayMenuItemsInjectable);
return getStartableStoppable("reactive-tray-menu-items", () =>
reaction(
() => reactiveItems.get().map(toNonReactiveItem),
(nonReactiveItems) => electronTray.setMenuItems(nonReactiveItems),
{
fireImmediately: true,
},
),
);
},
});
export default reactiveTrayMenuItemsInjectable;
const toNonReactiveItem = (item: TrayMenuItem): MinimalTrayMenuItem => ({
id: item.id,
parentId: item.parentId,
click: item.click,
tooltip: item.tooltip,
separator: item.separator,
enabled: item.enabled.get(),
label: item.label?.get(),
});
@@ -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 reactiveTrayMenuItemsInjectable from "./reactive-tray-menu-items.injectable";
import { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
import startTrayInjectable from "../electron-tray/start-tray.injectable";
const startReactiveTrayMenuItemsInjectable = getInjectable({
id: "start-reactive-tray-menu-items",
instantiate: (di) => {
const reactiveTrayMenuItems = di.inject(reactiveTrayMenuItemsInjectable);
return {
id: "start-reactive-tray-menu-items",
run: () => {
reactiveTrayMenuItems.start();
},
runAfter: di.inject(startTrayInjectable),
};
},
injectionToken: onLoadOfApplicationInjectionToken,
});
export default startReactiveTrayMenuItemsInjectable;
@@ -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 reactiveTrayMenuItemsInjectable from "./reactive-tray-menu-items.injectable";
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
const stopReactiveTrayMenuItemsInjectable = getInjectable({
id: "stop-reactive-tray-menu-items",
instantiate: (di) => {
const reactiveTrayMenuItems = di.inject(reactiveTrayMenuItemsInjectable);
return {
id: "stop-reactive-tray-menu-items",
run: () => void reactiveTrayMenuItems.stop(),
};
},
injectionToken: beforeQuitOfBackEndInjectionToken,
});
export default stopReactiveTrayMenuItemsInjectable;
@@ -0,0 +1,46 @@
/**
* 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 showApplicationWindowInjectable from "../../../start-main-application/lens-window/show-application-window.injectable";
import showAboutInjectable from "../../../../features/application-menu/main/menu-items/special-menu-for-mac-application/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";
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
import { pipeline } from "@ogre-tools/fp";
import productNameInjectable from "../../../../common/vars/product-name.injectable";
const aboutAppTrayItemInjectable = getInjectable({
id: "about-app-tray-item",
instantiate: (di) => {
const productName = di.inject(productNameInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const showAbout = di.inject(showAboutInjectable);
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
return {
id: "about-app",
parentId: null,
orderNumber: 140,
label: computed(() => `About ${productName}`),
enabled: computed(() => true),
visible: computed(() => true),
click: pipeline(
async () => {
await showApplicationWindow();
showAbout();
},
withErrorLoggingFor(() => "[TRAY]: Opening of show about failed."),
withErrorSuppression,
),
};
},
injectionToken: trayMenuItemInjectionToken,
});
export default aboutAppTrayItemInjectable;
@@ -0,0 +1,41 @@
/**
* 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 { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
import showApplicationWindowInjectable from "../../../start-main-application/lens-window/show-application-window.injectable";
import { computed } from "mobx";
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
import { pipeline } from "@ogre-tools/fp";
import productNameInjectable from "../../../../common/vars/product-name.injectable";
const openAppTrayItemInjectable = getInjectable({
id: "open-app-tray-item",
instantiate: (di) => {
const productName = di.inject(productNameInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
return {
id: "open-app",
parentId: null,
label: computed(() => `Open ${productName}`),
orderNumber: 10,
enabled: computed(() => true),
visible: computed(() => true),
click: pipeline(
showApplicationWindow,
withErrorLoggingFor(() => "[TRAY]: Opening of application window failed."),
withErrorSuppression,
),
};
},
injectionToken: trayMenuItemInjectionToken,
});
export default openAppTrayItemInjectable;
@@ -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 { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
import { computed } from "mobx";
const quitAppSeparatorTrayItemInjectable = getInjectable({
id: "quit-app-separator-tray-item",
instantiate: () => ({
id: "quit-app-separator",
parentId: null,
orderNumber: 149,
enabled: computed(() => true),
visible: computed(() => true),
separator: true,
}),
injectionToken: trayMenuItemInjectionToken,
});
export default quitAppSeparatorTrayItemInjectable;
@@ -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 { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
import { computed } from "mobx";
import stopServicesAndExitAppInjectable from "../../../stop-services-and-exit-app.injectable";
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
import { pipeline } from "@ogre-tools/fp";
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
const quitAppTrayItemInjectable = getInjectable({
id: "quit-app-tray-item",
instantiate: (di) => {
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
return {
id: "quit-app",
parentId: null,
orderNumber: 150,
label: computed(() => "Quit App"),
enabled: computed(() => true),
visible: computed(() => true),
click: pipeline(
stopServicesAndExitApp,
withErrorLoggingFor(() => "[TRAY]: Quitting application failed."),
// TODO: Find out how to improve typing so that instead of
// x => withErrorSuppression(x) there could only be withErrorSuppression
(x) => withErrorSuppression(x),
),
};
},
injectionToken: trayMenuItemInjectionToken,
});
export default quitAppTrayItemInjectable;
@@ -0,0 +1,23 @@
/**
* 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";
import type { IComputedValue } from "mobx";
export interface TrayMenuItem {
id: string;
parentId: string | null;
orderNumber: number;
enabled: IComputedValue<boolean>;
visible: IComputedValue<boolean>;
label?: IComputedValue<string>;
click?: () => Promise<void> | void;
tooltip?: string;
separator?: boolean;
}
export const trayMenuItemInjectionToken = getInjectionToken<TrayMenuItem>({
id: "tray-menu-item",
});
@@ -0,0 +1,123 @@
/**
* 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 type { Injectable } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
import type { LensMainExtension } from "../../../extensions/lens-main-extension";
import type { TrayMenuItem } from "./tray-menu-item-injection-token";
import { trayMenuItemInjectionToken } from "./tray-menu-item-injection-token";
import type { TrayMenuRegistration } from "../tray-menu-registration";
import { withErrorSuppression } from "../../../common/utils/with-error-suppression/with-error-suppression";
import type { WithErrorLoggingFor } from "../../../common/utils/with-error-logging/with-error-logging.injectable";
import withErrorLoggingInjectable from "../../../common/utils/with-error-logging/with-error-logging.injectable";
import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable";
import { isBoolean, isString } from "../../../common/utils";
const trayMenuItemRegistratorInjectable = getInjectable({
id: "tray-menu-item-registrator",
instantiate: (di) => (extension) => {
const mainExtension = extension as LensMainExtension;
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
const getRandomId = di.inject(getRandomIdInjectable);
return computed(() => {
const trayMenus = Array.isArray(mainExtension.trayMenus) ? mainExtension.trayMenus : mainExtension.trayMenus.get();
return trayMenus.flatMap(
toItemInjectablesFor(mainExtension, withErrorLoggingFor, getRandomId),
);
});
},
injectionToken: extensionRegistratorInjectionToken,
});
export default trayMenuItemRegistratorInjectable;
const toItemInjectablesFor = (extension: LensMainExtension, withErrorLoggingFor: WithErrorLoggingFor, getRandomId: () => string) => {
const _toItemInjectables = (parentId: string | null) => (registration: TrayMenuRegistration): Injectable<TrayMenuItem, TrayMenuItem, void>[] => {
const trayItemId = registration.id || getRandomId();
const id = `${trayItemId}-tray-menu-item-for-extension-${extension.sanitizedExtensionId}`;
const parentInjectable = getInjectable({
id,
instantiate: () => ({
id,
parentId,
orderNumber: 100,
separator: registration.type === "separator",
label: computed(() => {
if (!registration.label) {
return "";
}
if (isString(registration.label)) {
return registration.label;
}
return registration.label.get();
}),
tooltip: registration.toolTip,
click: () => {
const decorated = pipeline(
registration.click || (() => {}),
withErrorLoggingFor(
() =>
`[TRAY]: Clicking of tray item "${trayItemId}" from extension "${extension.sanitizedExtensionId}" failed.`,
),
// TODO: Find out how to improve typing so that instead of
// x => withErrorSuppression(x) there could only be withErrorSuppression
(x) => withErrorSuppression(x),
);
return decorated(registration);
},
enabled: computed(() => {
if (registration.enabled === undefined) {
return true;
}
if (isBoolean(registration.enabled)) {
return registration.enabled;
}
return registration.enabled.get();
}),
visible: computed(() => {
if (!registration.visible) {
return true;
}
return registration.visible.get();
}),
}),
injectionToken: trayMenuItemInjectionToken,
});
const childMenuItems = registration.submenu || [];
const childInjectables = childMenuItems.flatMap(_toItemInjectables(id));
return [
parentInjectable,
...childInjectables,
];
};
return _toItemInjectables(null);
};
@@ -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 { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import { computed } from "mobx";
import { trayMenuItemInjectionToken } from "./tray-menu-item-injection-token";
import { pipeline } from "@ogre-tools/fp";
import { filter, sortBy } from "lodash/fp";
const trayMenuItemsInjectable = getInjectable({
id: "tray-menu-items",
instantiate: (di) => {
const computedInjectMany = di.inject(
computedInjectManyInjectable,
);
const reactiveMenuItems = computedInjectMany(trayMenuItemInjectionToken);
return computed(() =>
pipeline(
reactiveMenuItems.get(),
filter((item) => item.visible.get()),
(items) => sortBy("orderNumber", items),
),
);
},
});
export default trayMenuItemsInjectable;
@@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { IComputedValue } from "mobx";
export interface TrayMenuRegistration {
label?: string | IComputedValue<string>;
click?: (menuItem: TrayMenuRegistration) => void;
id?: string;
type?: "normal" | "separator" | "submenu";
toolTip?: string;
enabled?: boolean | IComputedValue<boolean>;
submenu?: TrayMenuRegistration[];
visible?: IComputedValue<boolean>;
}