mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Respond to PR comments
- Revert changes to structure of electronTrayInjectable - Add some behavioural tests for the tray icon Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
9f3140db77
commit
b7a2bb5385
@ -44,7 +44,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_13"
|
data-testid="close-notification-for-notification_16"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -95,7 +95,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_96"
|
data-testid="close-notification-for-notification_115"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -135,7 +135,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_99"
|
data-testid="close-notification-for-notification_118"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -186,7 +186,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_149"
|
data-testid="close-notification-for-notification_183"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -226,7 +226,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_152"
|
data-testid="close-notification-for-notification_186"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -266,7 +266,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_157"
|
data-testid="close-notification-for-notification_191"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -317,7 +317,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_215"
|
data-testid="close-notification-for-notification_266"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -357,7 +357,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_218"
|
data-testid="close-notification-for-notification_269"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -478,7 +478,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_48"
|
data-testid="close-notification-for-notification_59"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -518,7 +518,7 @@ exports[`installing update using tray when started when user checks for updates
|
|||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon close material interactive focusable"
|
class="Icon close material interactive focusable"
|
||||||
data-testid="close-notification-for-notification_51"
|
data-testid="close-notification-for-notification_62"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@ -15,12 +15,15 @@ import type { DownloadPlatformUpdate } from "../../main/application-update/downl
|
|||||||
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
import showApplicationWindowInjectable from "../../main/start-main-application/lens-window/show-application-window.injectable";
|
import showApplicationWindowInjectable from "../../main/start-main-application/lens-window/show-application-window.injectable";
|
||||||
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||||
|
import type { TrayIconPaths } from "../../main/tray/tray-icon-path.injectable";
|
||||||
|
import trayIconPathsInjectable from "../../main/tray/tray-icon-path.injectable";
|
||||||
|
|
||||||
describe("installing update using tray", () => {
|
describe("installing update using tray", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
|
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
|
||||||
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
|
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
|
||||||
let showApplicationWindowMock: jest.Mock;
|
let showApplicationWindowMock: jest.Mock;
|
||||||
|
let trayIconPaths: TrayIconPaths;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
applicationBuilder = getApplicationBuilder();
|
applicationBuilder = getApplicationBuilder();
|
||||||
@ -44,6 +47,7 @@ describe("installing update using tray", () => {
|
|||||||
|
|
||||||
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
mainDi.override(publishIsConfiguredInjectable, () => true);
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
trayIconPaths = mainDi.inject(trayIconPathsInjectable);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -58,6 +62,10 @@ describe("installing update using tray", () => {
|
|||||||
expect(rendered.baseElement).toMatchSnapshot();
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should use the normal tray icon", () => {
|
||||||
|
expect(applicationBuilder.tray.getIconPath()).toBe(trayIconPaths.normal);
|
||||||
|
});
|
||||||
|
|
||||||
it("user cannot install update yet", () => {
|
it("user cannot install update yet", () => {
|
||||||
expect(applicationBuilder.tray.get("install-update")).toBeNull();
|
expect(applicationBuilder.tray.get("install-update")).toBeNull();
|
||||||
});
|
});
|
||||||
@ -73,15 +81,19 @@ describe("installing update using tray", () => {
|
|||||||
expect(showApplicationWindowMock).not.toHaveBeenCalled();
|
expect(showApplicationWindowMock).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should still use the normal tray icon", () => {
|
||||||
|
expect(applicationBuilder.tray.getIconPath()).toBe(trayIconPaths.normal);
|
||||||
|
});
|
||||||
|
|
||||||
it("user cannot check for updates again", () => {
|
it("user cannot check for updates again", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.enabled,
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("name of tray item for checking updates indicates that checking is happening", () => {
|
it("name of tray item for checking updates indicates that checking is happening", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.label,
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
).toBe("Checking for updates...");
|
).toBe("Checking for updates...");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,19 +118,23 @@ describe("installing update using tray", () => {
|
|||||||
expect(showApplicationWindowMock).toHaveBeenCalled();
|
expect(showApplicationWindowMock).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should still use the normal tray icon", () => {
|
||||||
|
expect(applicationBuilder.tray.getIconPath()).toBe(trayIconPaths.normal);
|
||||||
|
});
|
||||||
|
|
||||||
it("user cannot install update", () => {
|
it("user cannot install update", () => {
|
||||||
expect(applicationBuilder.tray.get("install-update")).toBeNull();
|
expect(applicationBuilder.tray.get("install-update")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("user can check for updates again", () => {
|
it("user can check for updates again", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.enabled,
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("name of tray item for checking updates no longer indicates that checking is happening", () => {
|
it("name of tray item for checking updates no longer indicates that checking is happening", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.label,
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
).toBe("Check for updates");
|
).toBe("Check for updates");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -141,15 +157,19 @@ describe("installing update using tray", () => {
|
|||||||
expect(showApplicationWindowMock).toHaveBeenCalled();
|
expect(showApplicationWindowMock).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should use the update available icon", () => {
|
||||||
|
expect(applicationBuilder.tray.getIconPath()).toBe(trayIconPaths.updateAvailable);
|
||||||
|
});
|
||||||
|
|
||||||
it("user cannot check for updates again yet", () => {
|
it("user cannot check for updates again yet", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.enabled,
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("name of tray item for checking updates indicates that downloading is happening", () => {
|
it("name of tray item for checking updates indicates that downloading is happening", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.label,
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
).toBe("Downloading update some-version (0%)...");
|
).toBe("Downloading update some-version (0%)...");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -161,7 +181,7 @@ describe("installing update using tray", () => {
|
|||||||
progressOfUpdateDownload.set({ percentage: 42.424242 });
|
progressOfUpdateDownload.set({ percentage: 42.424242 });
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.label,
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
).toBe("Downloading update some-version (42%)...");
|
).toBe("Downloading update some-version (42%)...");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -184,15 +204,19 @@ describe("installing update using tray", () => {
|
|||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should revert to use the normal tray icon", () => {
|
||||||
|
expect(applicationBuilder.tray.getIconPath()).toBe(trayIconPaths.normal);
|
||||||
|
});
|
||||||
|
|
||||||
it("user can check for updates again", () => {
|
it("user can check for updates again", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.enabled,
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.label,
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
).toBe("Check for updates");
|
).toBe("Check for updates");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -208,19 +232,23 @@ describe("installing update using tray", () => {
|
|||||||
|
|
||||||
it("user can install update", () => {
|
it("user can install update", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("install-update")?.label,
|
applicationBuilder.tray.get("install-update")?.label?.get(),
|
||||||
).toBe("Install update some-version");
|
).toBe("Install update some-version");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should use the update available icon", () => {
|
||||||
|
expect(applicationBuilder.tray.getIconPath()).toBe(trayIconPaths.updateAvailable);
|
||||||
|
});
|
||||||
|
|
||||||
it("user can check for updates again", () => {
|
it("user can check for updates again", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.enabled,
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
||||||
expect(
|
expect(
|
||||||
applicationBuilder.tray.get("check-for-updates")?.label,
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
).toBe("Check for updates");
|
).toBe("Check for updates");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
export type UpdateChannelId = "alpha" | "beta" | "latest";
|
export type UpdateChannelId = "alpha" | "beta" | "latest";
|
||||||
|
|
||||||
const latestChannel: UpdateChannel = {
|
const latestChannel: UpdateChannel = {
|
||||||
|
|||||||
@ -4,12 +4,21 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
import type { JsonValue } from "type-fest";
|
|
||||||
|
|
||||||
export interface SyncBox<TValue extends JsonValue> {
|
type AsJson<T> = T extends string | number | boolean | null
|
||||||
|
? T
|
||||||
|
: T extends Function
|
||||||
|
? never
|
||||||
|
: T extends Array<infer V>
|
||||||
|
? AsJson<V>[]
|
||||||
|
: T extends object
|
||||||
|
? { [K in keyof T]: AsJson<T[K]> }
|
||||||
|
: never;
|
||||||
|
|
||||||
|
export interface SyncBox<TValue> {
|
||||||
id: string;
|
id: string;
|
||||||
value: IComputedValue<TValue>;
|
value: IComputedValue<AsJson<TValue>>;
|
||||||
set: (value: TValue) => void;
|
set: (value: AsJson<TValue>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const syncBoxInjectionToken = getInjectionToken<SyncBox<any>>({
|
export const syncBoxInjectionToken = getInjectionToken<SyncBox<any>>({
|
||||||
|
|||||||
@ -1,17 +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 type { MenuItemConstructorOptions } from "electron";
|
|
||||||
import { Menu } from "electron";
|
|
||||||
|
|
||||||
export type BuildMenuFromTemplate = (template: MenuItemConstructorOptions[]) => Menu;
|
|
||||||
|
|
||||||
const buildMenuFromTemplateInjectable = getInjectable({
|
|
||||||
id: "build-menu-from-template",
|
|
||||||
instantiate: (): BuildMenuFromTemplate => (template) => Menu.buildFromTemplate(template),
|
|
||||||
causesSideEffects: true, // Not really but isn't defined
|
|
||||||
});
|
|
||||||
|
|
||||||
export default buildMenuFromTemplateInjectable;
|
|
||||||
@ -3,20 +3,28 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { Menu } from "electron";
|
import { Menu, Tray } from "electron";
|
||||||
import { Tray } from "electron";
|
|
||||||
import packageJsonInjectable from "../../../common/vars/package-json.injectable";
|
import packageJsonInjectable from "../../../common/vars/package-json.injectable";
|
||||||
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
|
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
|
||||||
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||||
import loggerInjectable from "../../../common/logger.injectable";
|
import loggerInjectable from "../../../common/logger.injectable";
|
||||||
import trayIconPathsInjectable from "../tray-icon-path.injectable";
|
import trayIconPathsInjectable from "../tray-icon-path.injectable";
|
||||||
|
import type { TrayMenuItem } from "../tray-menu-item/tray-menu-item-injection-token";
|
||||||
|
import { convertToElectronMenuTemplate } from "../reactive-tray-menu-items/converters";
|
||||||
|
|
||||||
const TRAY_LOG_PREFIX = "[TRAY]";
|
const TRAY_LOG_PREFIX = "[TRAY]";
|
||||||
|
|
||||||
|
export interface ElectronTray {
|
||||||
|
start(): void;
|
||||||
|
stop(): void;
|
||||||
|
setMenuItems(menuItems: TrayMenuItem[]): void;
|
||||||
|
setIconPath(iconPath: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
const electronTrayInjectable = getInjectable({
|
const electronTrayInjectable = getInjectable({
|
||||||
id: "electron-tray",
|
id: "electron-tray",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di): ElectronTray => {
|
||||||
const packageJson = di.inject(packageJsonInjectable);
|
const packageJson = di.inject(packageJsonInjectable);
|
||||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||||
const isWindows = di.inject(isWindowsInjectable);
|
const isWindows = di.inject(isWindowsInjectable);
|
||||||
@ -42,10 +50,13 @@ const electronTrayInjectable = getInjectable({
|
|||||||
stop: () => {
|
stop: () => {
|
||||||
tray.destroy();
|
tray.destroy();
|
||||||
},
|
},
|
||||||
setMenu: (menu: Menu) => {
|
setMenuItems: (menuItems) => {
|
||||||
|
const template = convertToElectronMenuTemplate(menuItems);
|
||||||
|
const menu = Menu.buildFromTemplate(template);
|
||||||
|
|
||||||
tray.setContextMenu(menu);
|
tray.setContextMenu(menu);
|
||||||
},
|
},
|
||||||
setIconPath: (iconPath: string) => {
|
setIconPath: (iconPath) => {
|
||||||
tray.setImage(iconPath);
|
tray.setImage(iconPath);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,63 +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 loggerInjectable from "../../../common/logger.injectable";
|
|
||||||
import type { TrayMenuItem } from "../tray-menu-item/tray-menu-item-injection-token";
|
|
||||||
|
|
||||||
const convertToElectronMenuTemplateInjectable = getInjectable({
|
|
||||||
id: "convert-to-electron-menu-template",
|
|
||||||
instantiate: (di) => {
|
|
||||||
const logger = di.inject(loggerInjectable);
|
|
||||||
|
|
||||||
return (trayMenuItems: TrayMenuItem[]) => {
|
|
||||||
const toTrayMenuOptions = (parentId: string | null) => (
|
|
||||||
trayMenuItems
|
|
||||||
.filter((item) => item.parentId === parentId)
|
|
||||||
.map((trayMenuItem: TrayMenuItem): Electron.MenuItemConstructorOptions => {
|
|
||||||
if (trayMenuItem.separator) {
|
|
||||||
return { id: trayMenuItem.id, type: "separator" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const childItems = toTrayMenuOptions(trayMenuItem.id);
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: trayMenuItem.id,
|
|
||||||
label: trayMenuItem.label?.get(),
|
|
||||||
enabled: trayMenuItem.enabled.get(),
|
|
||||||
toolTip: trayMenuItem.tooltip,
|
|
||||||
|
|
||||||
...(childItems.length === 0
|
|
||||||
? {
|
|
||||||
type: "normal",
|
|
||||||
submenu: toTrayMenuOptions(trayMenuItem.id),
|
|
||||||
|
|
||||||
click: () => {
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
await trayMenuItem.click?.();
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(
|
|
||||||
`[TRAY]: clicking item "${trayMenuItem.id} failed."`,
|
|
||||||
{ error },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
type: "submenu",
|
|
||||||
submenu: toTrayMenuOptions(trayMenuItem.id),
|
|
||||||
}),
|
|
||||||
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return toTrayMenuOptions(null);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default convertToElectronMenuTemplateInjectable;
|
|
||||||
40
src/main/tray/reactive-tray-menu-items/converters.ts
Normal file
40
src/main/tray/reactive-tray-menu-items/converters.ts
Normal file
@ -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 { TrayMenuItem } from "../tray-menu-item/tray-menu-item-injection-token";
|
||||||
|
|
||||||
|
export function convertToElectronMenuTemplate(trayMenuItems: TrayMenuItem[]): Electron.MenuItemConstructorOptions[] {
|
||||||
|
const toTrayMenuOptions = (parentId: string | null) => (
|
||||||
|
trayMenuItems
|
||||||
|
.filter((item) => item.parentId === parentId)
|
||||||
|
.map((trayMenuItem: TrayMenuItem): Electron.MenuItemConstructorOptions => {
|
||||||
|
if (trayMenuItem.separator) {
|
||||||
|
return { id: trayMenuItem.id, type: "separator" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const childItems = toTrayMenuOptions(trayMenuItem.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: trayMenuItem.id,
|
||||||
|
label: trayMenuItem.label?.get(),
|
||||||
|
enabled: trayMenuItem.enabled.get(),
|
||||||
|
toolTip: trayMenuItem.tooltip,
|
||||||
|
|
||||||
|
...(childItems.length === 0
|
||||||
|
? {
|
||||||
|
type: "normal",
|
||||||
|
submenu: toTrayMenuOptions(trayMenuItem.id),
|
||||||
|
click: trayMenuItem.click,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: "submenu",
|
||||||
|
submenu: toTrayMenuOptions(trayMenuItem.id),
|
||||||
|
}),
|
||||||
|
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return toTrayMenuOptions(null);
|
||||||
|
}
|
||||||
@ -6,19 +6,19 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||||
import { reaction } from "mobx";
|
import { reaction } from "mobx";
|
||||||
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
|
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
|
||||||
import trayMenuInjectable from "./tray-menu.injectable";
|
import trayMenuItemsInjectable from "../tray-menu-item/tray-menu-items.injectable";
|
||||||
|
|
||||||
const reactiveTrayMenuItemsInjectable = getInjectable({
|
const reactiveTrayMenuItemsInjectable = getInjectable({
|
||||||
id: "reactive-tray-menu-items",
|
id: "reactive-tray-menu-items",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const electronTray = di.inject(electronTrayInjectable);
|
const electronTray = di.inject(electronTrayInjectable);
|
||||||
const trayMenu = di.inject(trayMenuInjectable);
|
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
||||||
|
|
||||||
return getStartableStoppable("reactive-tray-menu-items", () => (
|
return getStartableStoppable("reactive-tray-menu-items", () => (
|
||||||
reaction(
|
reaction(
|
||||||
() => trayMenu.get(),
|
() => trayMenuItems.get(),
|
||||||
electronTray.setMenu,
|
electronTray.setMenuItems,
|
||||||
{
|
{
|
||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,28 +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 trayMenuItemsInjectable from "../tray-menu-item/tray-menu-items.injectable";
|
|
||||||
import buildMenuFromTemplateInjectable from "../../electron/build-from-template.injectable";
|
|
||||||
import convertToElectronMenuTemplateInjectable from "./convert-to-electron-menu-template.injectable";
|
|
||||||
|
|
||||||
const trayMenuInjectable = getInjectable({
|
|
||||||
id: "tray-menu",
|
|
||||||
instantiate: (di) => {
|
|
||||||
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
|
||||||
const convertToElectronMenuTemplate = di.inject(convertToElectronMenuTemplateInjectable);
|
|
||||||
const buildMenuFromTemplate = di.inject(buildMenuFromTemplateInjectable);
|
|
||||||
|
|
||||||
return computed(() => (
|
|
||||||
buildMenuFromTemplate(
|
|
||||||
convertToElectronMenuTemplate(
|
|
||||||
trayMenuItems.get(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default trayMenuInjectable;
|
|
||||||
@ -8,10 +8,15 @@ import staticFilesDirectoryInjectable from "../../common/vars/static-files-direc
|
|||||||
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
|
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
|
||||||
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
||||||
|
|
||||||
|
export interface TrayIconPaths {
|
||||||
|
normal: string;
|
||||||
|
updateAvailable: string;
|
||||||
|
}
|
||||||
|
|
||||||
const trayIconPathsInjectable = getInjectable({
|
const trayIconPathsInjectable = getInjectable({
|
||||||
id: "tray-icon-paths",
|
id: "tray-icon-paths",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di): TrayIconPaths => {
|
||||||
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
||||||
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
|
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
|
||||||
const isDevelopment = di.inject(isDevelopmentInjectable);
|
const isDevelopment = di.inject(isDevelopmentInjectable);
|
||||||
|
|||||||
@ -24,12 +24,12 @@ import type { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
|||||||
import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable";
|
import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable";
|
||||||
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
|
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import { flatMap, compact, join, get, filter, map } from "lodash/fp";
|
import { flatMap, compact, join, get, filter, map, matches, find } from "lodash/fp";
|
||||||
import preferenceNavigationItemsInjectable from "../+preferences/preferences-navigation/preference-navigation-items.injectable";
|
import preferenceNavigationItemsInjectable from "../+preferences/preferences-navigation/preference-navigation-items.injectable";
|
||||||
import navigateToPreferencesInjectable from "../../../common/front-end-routing/routes/preferences/navigate-to-preferences.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 type { MenuItemOpts } from "../../../main/menu/application-menu-items.injectable";
|
||||||
import applicationMenuItemsInjectable from "../../../main/menu/application-menu-items.injectable";
|
import applicationMenuItemsInjectable from "../../../main/menu/application-menu-items.injectable";
|
||||||
import type { MenuItemConstructorOptions, MenuItem, Menu } from "electron";
|
import type { MenuItemConstructorOptions, MenuItem } from "electron";
|
||||||
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||||
import navigateToHelmChartsInjectable 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";
|
||||||
import hostedClusterInjectable from "../../../common/cluster-store/hosted-cluster.injectable";
|
import hostedClusterInjectable from "../../../common/cluster-store/hosted-cluster.injectable";
|
||||||
@ -50,7 +50,8 @@ import broadcastThatRootFrameIsRenderedInjectable from "../../frames/root-frame/
|
|||||||
import { getDiForUnitTesting as getRendererDi } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting as getRendererDi } from "../../getDiForUnitTesting";
|
||||||
import { getDiForUnitTesting as getMainDi } from "../../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting as getMainDi } from "../../../main/getDiForUnitTesting";
|
||||||
import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels";
|
import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels";
|
||||||
import buildMenuFromTemplateInjectable from "../../../main/electron/build-from-template.injectable";
|
import type { TrayMenuItem } from "../../../main/tray/tray-menu-item/tray-menu-item-injection-token";
|
||||||
|
import trayIconPathsInjectable from "../../../main/tray/tray-icon-path.injectable";
|
||||||
|
|
||||||
type Callback = (dis: DiContainers) => void | Promise<void>;
|
type Callback = (dis: DiContainers) => void | Promise<void>;
|
||||||
|
|
||||||
@ -65,7 +66,8 @@ export interface ApplicationBuilder {
|
|||||||
|
|
||||||
tray: {
|
tray: {
|
||||||
click: (id: string) => Promise<void>;
|
click: (id: string) => Promise<void>;
|
||||||
get: (id: string) => Electron.MenuItem | null;
|
get: (id: string) => TrayMenuItem | null;
|
||||||
|
getIconPath: () => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
applicationMenu: {
|
applicationMenu: {
|
||||||
@ -96,10 +98,6 @@ interface Environment {
|
|||||||
onAllowKubeResource: () => void;
|
onAllowKubeResource: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllSubMenuItems = (item: MenuItem): MenuItem[] => {
|
|
||||||
return [item, ...(item.submenu?.items ?? []).flatMap(getAllSubMenuItems)];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getApplicationBuilder = () => {
|
export const getApplicationBuilder = () => {
|
||||||
const mainDi = getMainDi({
|
const mainDi = getMainDi({
|
||||||
doGeneralOverrides: true,
|
doGeneralOverrides: true,
|
||||||
@ -170,84 +168,22 @@ export const getApplicationBuilder = () => {
|
|||||||
computed(() => []),
|
computed(() => []),
|
||||||
);
|
);
|
||||||
|
|
||||||
let commandId = 0;
|
const iconPaths = mainDi.inject(trayIconPathsInjectable);
|
||||||
const makeFakeMenuItem = (opts: MenuItemConstructorOptions, menu: Menu): MenuItem => {
|
|
||||||
const menuItemFake: MenuItem = {
|
|
||||||
accelerator: opts.accelerator,
|
|
||||||
checked: opts.checked ?? false,
|
|
||||||
click: () => opts.click?.(menuItemFake, undefined, new KeyboardEvent("fake")),
|
|
||||||
commandId: commandId += 1,
|
|
||||||
enabled: opts.enabled ?? false,
|
|
||||||
icon: opts.icon,
|
|
||||||
id: opts.id ?? "",
|
|
||||||
label: opts.label ?? "",
|
|
||||||
menu,
|
|
||||||
registerAccelerator: opts.registerAccelerator ?? true,
|
|
||||||
sharingItem: opts.sharingItem ?? {},
|
|
||||||
sublabel: opts.sublabel ?? "",
|
|
||||||
toolTip: opts.toolTip ?? "",
|
|
||||||
type: opts.type ?? "normal",
|
|
||||||
visible: opts.visible ?? true,
|
|
||||||
role: opts.role,
|
|
||||||
submenu: opts.submenu === undefined
|
|
||||||
? undefined
|
|
||||||
: Array.isArray(opts.submenu)
|
|
||||||
? makeFakeMenu(opts.submenu)
|
|
||||||
: opts.submenu,
|
|
||||||
};
|
|
||||||
|
|
||||||
return menuItemFake;
|
let trayMenuItemsStateFake: TrayMenuItem[];
|
||||||
};
|
let trayMenuIconPath: string;
|
||||||
const makeFakeMenu = (templates: MenuItemConstructorOptions[]): Menu => {
|
|
||||||
const menuFake: Electron.Menu = {
|
|
||||||
addListener: () => {
|
|
||||||
throw new Error("Adding listeners is not supported currently");
|
|
||||||
},
|
|
||||||
on: () => {
|
|
||||||
throw new Error("Adding listeners is not supported currently");
|
|
||||||
},
|
|
||||||
once: () => {
|
|
||||||
throw new Error("Adding listeners is not supported currently");
|
|
||||||
},
|
|
||||||
removeListener: () => {
|
|
||||||
throw new Error("Removing listeners is not supported currently");
|
|
||||||
},
|
|
||||||
append: () => {
|
|
||||||
throw new Error("Adding new menu items is not supported currently");
|
|
||||||
},
|
|
||||||
insert: () => {
|
|
||||||
throw new Error("Adding new menu items is not supported currently");
|
|
||||||
},
|
|
||||||
popup: () => {
|
|
||||||
throw new Error("Popping up menu is not supported currently");
|
|
||||||
},
|
|
||||||
closePopup: () => {
|
|
||||||
throw new Error("Popping up menu is not supported currently");
|
|
||||||
},
|
|
||||||
get items() {
|
|
||||||
return [...menuItems];
|
|
||||||
},
|
|
||||||
getMenuItemById: (id) => menuItems
|
|
||||||
.flatMap(getAllSubMenuItems)
|
|
||||||
.find(menuItem => menuItem.id === id)
|
|
||||||
?? null,
|
|
||||||
};
|
|
||||||
const menuItems = templates.map(template => makeFakeMenuItem(template, menuFake));
|
|
||||||
|
|
||||||
return menuFake;
|
|
||||||
};
|
|
||||||
|
|
||||||
mainDi.override(buildMenuFromTemplateInjectable, () => makeFakeMenu);
|
|
||||||
|
|
||||||
let trayMenuStateFake: Electron.Menu | undefined;
|
|
||||||
|
|
||||||
mainDi.override(electronTrayInjectable, () => ({
|
mainDi.override(electronTrayInjectable, () => ({
|
||||||
start: () => {},
|
start: () => {
|
||||||
stop: () => {},
|
trayMenuIconPath = iconPaths.normal;
|
||||||
setMenu: (menu) => {
|
},
|
||||||
trayMenuStateFake = menu;
|
stop: () => {},
|
||||||
|
setMenuItems: (items) => {
|
||||||
|
trayMenuItemsStateFake = items;
|
||||||
|
},
|
||||||
|
setIconPath: (path) => {
|
||||||
|
trayMenuIconPath = path;
|
||||||
},
|
},
|
||||||
setIconPath: () => {},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let allowedResourcesState: IObservableArray<KubeResource>;
|
let allowedResourcesState: IObservableArray<KubeResource>;
|
||||||
@ -294,20 +230,18 @@ export const getApplicationBuilder = () => {
|
|||||||
|
|
||||||
tray: {
|
tray: {
|
||||||
get: (id: string) => {
|
get: (id: string) => {
|
||||||
return trayMenuStateFake?.getMenuItemById(id) ?? null;
|
return trayMenuItemsStateFake.find(matches({ id })) ?? null;
|
||||||
},
|
},
|
||||||
|
getIconPath: () => trayMenuIconPath,
|
||||||
click: async (id: string) => {
|
click: async (id: string) => {
|
||||||
if (!trayMenuStateFake) {
|
const menuItem = pipeline(
|
||||||
throw new Error(`Tried to click tray menu with ID ${id}, but tray menu has not been set yet`);
|
trayMenuItemsStateFake,
|
||||||
}
|
find((menuItem) => menuItem.id === id),
|
||||||
|
);
|
||||||
const menuItem = trayMenuStateFake.getMenuItemById(id);
|
|
||||||
|
|
||||||
if (!menuItem) {
|
if (!menuItem) {
|
||||||
const availableIds = pipeline(
|
const availableIds = pipeline(
|
||||||
trayMenuStateFake.items,
|
trayMenuItemsStateFake,
|
||||||
flatMap(getAllSubMenuItems),
|
|
||||||
filter(item => !!item.click),
|
filter(item => !!item.click),
|
||||||
map(item => item.id),
|
map(item => item.id),
|
||||||
join(", "),
|
join(", "),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user