mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introduce competition for tray
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
44e0ebc10d
commit
35d6b54ed4
@ -46,7 +46,6 @@ import setupSentryInjectable from "./start-main-application/runnables/setup-sent
|
|||||||
import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable";
|
import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable";
|
||||||
import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable";
|
import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable";
|
||||||
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
|
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
|
||||||
import trayInjectable from "./tray/tray.injectable";
|
|
||||||
import applicationMenuInjectable from "./menu/application-menu.injectable";
|
import applicationMenuInjectable from "./menu/application-menu.injectable";
|
||||||
import isDevelopmentInjectable from "../common/vars/is-development.injectable";
|
import isDevelopmentInjectable from "../common/vars/is-development.injectable";
|
||||||
import setupSystemCaInjectable from "./start-main-application/runnables/setup-system-ca.injectable";
|
import setupSystemCaInjectable from "./start-main-application/runnables/setup-system-ca.injectable";
|
||||||
@ -81,6 +80,7 @@ import syncUpdateIsReadyToBeInstalledInjectable from "./electron-app/runnables/u
|
|||||||
import quitAndInstallUpdateInjectable from "./electron-app/features/quit-and-install-update.injectable";
|
import quitAndInstallUpdateInjectable from "./electron-app/features/quit-and-install-update.injectable";
|
||||||
import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable";
|
import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable";
|
||||||
import publishIsConfiguredInjectable from "./update-app/publish-is-configured.injectable";
|
import publishIsConfiguredInjectable from "./update-app/publish-is-configured.injectable";
|
||||||
|
import checkForPlatformUpdatesInjectable from "./update-app/check-for-platform-updates.injectable";
|
||||||
|
|
||||||
export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
|
export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
|
||||||
const {
|
const {
|
||||||
@ -125,7 +125,6 @@ export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
|
|||||||
di.override(stopServicesAndExitAppInjectable, () => () => {});
|
di.override(stopServicesAndExitAppInjectable, () => () => {});
|
||||||
di.override(lensResourcesDirInjectable, () => "/irrelevant");
|
di.override(lensResourcesDirInjectable, () => "/irrelevant");
|
||||||
|
|
||||||
di.override(trayInjectable, () => ({ start: () => {}, stop: () => {} }));
|
|
||||||
di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} }));
|
di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||||
|
|
||||||
// TODO: Remove usages of globally exported appEventBus to get rid of this
|
// TODO: Remove usages of globally exported appEventBus to get rid of this
|
||||||
@ -226,6 +225,10 @@ const overrideElectronFeatures = (di: DiContainer) => {
|
|||||||
di.override(syncUpdateIsReadyToBeInstalledInjectable, () => ({ start: () => {}, stop: () => {} }));
|
di.override(syncUpdateIsReadyToBeInstalledInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||||
di.override(quitAndInstallUpdateInjectable, () => () => {});
|
di.override(quitAndInstallUpdateInjectable, () => () => {});
|
||||||
|
|
||||||
|
di.override(checkForPlatformUpdatesInjectable, () => () => {
|
||||||
|
throw new Error("Tried to check for platform updates without explicit override.");
|
||||||
|
});
|
||||||
|
|
||||||
di.override(createElectronWindowForInjectable, () => () => async () => ({
|
di.override(createElectronWindowForInjectable, () => () => async () => ({
|
||||||
show: () => {},
|
show: () => {},
|
||||||
|
|
||||||
|
|||||||
116
src/main/tray/electron-tray/electron-tray.injectable.ts
Normal file
116
src/main/tray/electron-tray/electron-tray.injectable.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* 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 packageJsonInjectable from "../../../common/vars/package-json.injectable";
|
||||||
|
import logger from "../../logger";
|
||||||
|
import { TRAY_LOG_PREFIX } from "../tray";
|
||||||
|
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
|
||||||
|
import type { TrayMenuItem } from "../tray-menu-item/tray-menu-item-injection-token";
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { isEmpty, map, filter } from "lodash/fp";
|
||||||
|
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||||
|
import loggerInjectable from "../../../common/logger.injectable";
|
||||||
|
import trayIconPathInjectable from "../tray-icon-path.injectable";
|
||||||
|
|
||||||
|
const electronTrayInjectable = getInjectable({
|
||||||
|
id: "electron-tray",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const packageJson = di.inject(packageJsonInjectable);
|
||||||
|
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||||
|
const isWindows = di.inject(isWindowsInjectable);
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
const trayIconPath = di.inject(trayIconPathInjectable);
|
||||||
|
|
||||||
|
let tray: Tray;
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: () => {
|
||||||
|
tray = new Tray(trayIconPath);
|
||||||
|
|
||||||
|
tray.setToolTip(packageJson.description);
|
||||||
|
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: (items: TrayMenuItem[]) => {
|
||||||
|
pipeline(
|
||||||
|
items,
|
||||||
|
convertToElectronMenuTemplate,
|
||||||
|
Menu.buildFromTemplate,
|
||||||
|
|
||||||
|
(template) => {
|
||||||
|
tray.setContextMenu(template);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default electronTrayInjectable;
|
||||||
|
|
||||||
|
const convertToElectronMenuTemplate = (trayMenuItems: TrayMenuItem[]) => {
|
||||||
|
const _toTrayMenuOptions = (parentId: string | null) =>
|
||||||
|
pipeline(
|
||||||
|
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,
|
||||||
|
|
||||||
|
...(isEmpty(childItems)
|
||||||
|
? {
|
||||||
|
type: "normal",
|
||||||
|
submenu: _toTrayMenuOptions(trayMenuItem.id),
|
||||||
|
|
||||||
|
click: () => {
|
||||||
|
try {
|
||||||
|
trayMenuItem.click?.();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
`${TRAY_LOG_PREFIX}: clicking item "${trayMenuItem.id} failed."`,
|
||||||
|
{ error },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: "submenu",
|
||||||
|
submenu: _toTrayMenuOptions(trayMenuItem.id),
|
||||||
|
}),
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return _toTrayMenuOptions(null);
|
||||||
|
};
|
||||||
25
src/main/tray/electron-tray/start-tray.injectable.ts
Normal file
25
src/main/tray/electron-tray/start-tray.injectable.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
run: () => {
|
||||||
|
electronTray.start();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: onLoadOfApplicationInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default startTrayInjectable;
|
||||||
28
src/main/tray/electron-tray/stop-tray.injectable.ts
Normal file
28
src/main/tray/electron-tray/stop-tray.injectable.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
run: () => {
|
||||||
|
electronTray.stop();
|
||||||
|
},
|
||||||
|
|
||||||
|
runAfter: di.inject(stopReactiveTrayMenuItemsInjectable),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default stopTrayInjectable;
|
||||||
@ -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 { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||||
|
import { autorun } from "mobx";
|
||||||
|
import trayMenuItemsInjectable from "../tray-menu-item/tray-menu-items.injectable";
|
||||||
|
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
|
||||||
|
|
||||||
|
const reactiveTrayMenuItemsInjectable = getInjectable({
|
||||||
|
id: "reactive-tray-menu-items",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const electronTray = di.inject(electronTrayInjectable);
|
||||||
|
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
||||||
|
|
||||||
|
return getStartableStoppable("reactive-tray-menu-items", () => autorun(() => {
|
||||||
|
electronTray.setMenuItems(trayMenuItems.get());
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default reactiveTrayMenuItemsInjectable;
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
run: async () => {
|
||||||
|
await reactiveTrayMenuItems.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
runAfter: di.inject(startTrayInjectable),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: onLoadOfApplicationInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default startReactiveTrayMenuItemsInjectable;
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
run: async () => {
|
||||||
|
await reactiveTrayMenuItems.stop();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default stopReactiveTrayMenuItemsInjectable;
|
||||||
@ -47,6 +47,7 @@ import historyInjectable from "../../navigation/history.injectable";
|
|||||||
import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable";
|
import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable";
|
||||||
import type { TrayMenuItem } from "../../../main/tray/tray-menu-item/tray-menu-item-injection-token";
|
import type { TrayMenuItem } from "../../../main/tray/tray-menu-item/tray-menu-item-injection-token";
|
||||||
import updateIsAvailableStateInjectable from "../../../main/update-app/update-is-ready-to-be-installed-state.injectable";
|
import updateIsAvailableStateInjectable from "../../../main/update-app/update-is-ready-to-be-installed-state.injectable";
|
||||||
|
import electronTrayInjectable from "../../../main/tray/electron-tray/electron-tray.injectable";
|
||||||
|
|
||||||
type Callback = (dis: DiContainers) => void | Promise<void>;
|
type Callback = (dis: DiContainers) => void | Promise<void>;
|
||||||
|
|
||||||
@ -150,6 +151,17 @@ export const getApplicationBuilder = () => {
|
|||||||
computed(() => []),
|
computed(() => []),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let trayMenuItemsStateFake: TrayMenuItem[];
|
||||||
|
|
||||||
|
mainDi.override(electronTrayInjectable, () => ({
|
||||||
|
start: () => {},
|
||||||
|
stop: () => {},
|
||||||
|
|
||||||
|
setMenuItems: (items) => {
|
||||||
|
trayMenuItemsStateFake = items;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
let allowedResourcesState: IObservableArray<KubeResource>;
|
let allowedResourcesState: IObservableArray<KubeResource>;
|
||||||
let rendered: RenderResult;
|
let rendered: RenderResult;
|
||||||
|
|
||||||
@ -202,26 +214,18 @@ export const getApplicationBuilder = () => {
|
|||||||
|
|
||||||
tray: {
|
tray: {
|
||||||
get: (id: string) => {
|
get: (id: string) => {
|
||||||
const trayMenuItems = mainDi.inject(
|
return trayMenuItemsStateFake.find(matches({ id }));
|
||||||
trayMenuItemsInjectable,
|
|
||||||
);
|
|
||||||
|
|
||||||
return trayMenuItems.get().find(matches({ id }));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
click: async (id: string) => {
|
click: async (id: string) => {
|
||||||
const trayMenuItems = mainDi.inject(
|
|
||||||
trayMenuItemsInjectable,
|
|
||||||
);
|
|
||||||
|
|
||||||
const menuItem = pipeline(
|
const menuItem = pipeline(
|
||||||
trayMenuItems.get(),
|
trayMenuItemsStateFake,
|
||||||
find((menuItem) => menuItem.id === id),
|
find((menuItem) => menuItem.id === id),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!menuItem) {
|
if (!menuItem) {
|
||||||
const availableIds = pipeline(
|
const availableIds = pipeline(
|
||||||
trayMenuItems.get(),
|
trayMenuItemsStateFake,
|
||||||
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