mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make initTray injectable
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
432fc534c6
commit
60498bbd4a
14
src/main/electron/native-theme.injectable.ts
Normal file
14
src/main/electron/native-theme.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 { nativeTheme } from "electron";
|
||||||
|
|
||||||
|
const nativeThemeInjectable = getInjectable({
|
||||||
|
id: "native-theme",
|
||||||
|
instantiate: () => nativeTheme,
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default nativeThemeInjectable;
|
||||||
@ -39,7 +39,7 @@ import { initializeSentryReporting } from "../common/sentry";
|
|||||||
import { ensureDir } from "fs-extra";
|
import { ensureDir } from "fs-extra";
|
||||||
import { initMenu } from "./menu/menu";
|
import { initMenu } from "./menu/menu";
|
||||||
import { kubeApiUpgradeRequest } from "./proxy-functions";
|
import { kubeApiUpgradeRequest } from "./proxy-functions";
|
||||||
import { initTray } from "./tray/tray";
|
import initTrayInjectable from "./tray/init-tray.injectable";
|
||||||
import { ShellSession } from "./shell-session/shell-session";
|
import { ShellSession } from "./shell-session/shell-session";
|
||||||
import { getDi } from "./getDi";
|
import { getDi } from "./getDi";
|
||||||
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
|
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
|
||||||
@ -53,10 +53,8 @@ import clusterStoreInjectable from "../common/cluster-store/cluster-store.inject
|
|||||||
import routerInjectable from "./router/router.injectable";
|
import routerInjectable from "./router/router.injectable";
|
||||||
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||||
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||||
import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable";
|
|
||||||
import { broadcastNativeThemeOnUpdate } from "./native-theme";
|
import { broadcastNativeThemeOnUpdate } from "./native-theme";
|
||||||
import windowManagerInjectable from "./window-manager.injectable";
|
import windowManagerInjectable from "./window-manager.injectable";
|
||||||
import navigateToPreferencesInjectable from "../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
|
||||||
import syncGeneralCatalogEntitiesInjectable from "./catalog-sources/sync-general-catalog-entities.injectable";
|
import syncGeneralCatalogEntitiesInjectable from "./catalog-sources/sync-general-catalog-entities.injectable";
|
||||||
import hotbarStoreInjectable from "../common/hotbar-store.injectable";
|
import hotbarStoreInjectable from "../common/hotbar-store.injectable";
|
||||||
import applicationMenuItemsInjectable from "./menu/application-menu-items.injectable";
|
import applicationMenuItemsInjectable from "./menu/application-menu-items.injectable";
|
||||||
@ -300,12 +298,11 @@ async function main(di: DiContainer) {
|
|||||||
const windowManager = di.inject(windowManagerInjectable);
|
const windowManager = di.inject(windowManagerInjectable);
|
||||||
|
|
||||||
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
||||||
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
const initTray = di.inject(initTrayInjectable);
|
||||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
|
||||||
|
|
||||||
onQuitCleanup.push(
|
onQuitCleanup.push(
|
||||||
initMenu(applicationMenuItems),
|
initMenu(applicationMenuItems),
|
||||||
await initTray(windowManager, trayMenuItems, navigateToPreferences),
|
await initTray(),
|
||||||
() => ShellSession.cleanup(),
|
() => ShellSession.cleanup(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
23
src/main/tray/create-current-tray-icon.injectable.ts
Normal file
23
src/main/tray/create-current-tray-icon.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 nativeThemeInjectable from "../electron/native-theme.injectable";
|
||||||
|
import { createTrayIcon } from "./create-tray-icon";
|
||||||
|
import LogoLens from "../../renderer/components/icon/logo-lens.svg";
|
||||||
|
|
||||||
|
const createCurrentTrayIconInjectable = getInjectable({
|
||||||
|
id: "create-current-tray-icon",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const nativeTheme = di.inject(nativeThemeInjectable);
|
||||||
|
|
||||||
|
return () => createTrayIcon({
|
||||||
|
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
|
||||||
|
size: 16,
|
||||||
|
sourceSvg: LogoLens,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createCurrentTrayIconInjectable;
|
||||||
35
src/main/tray/create-tray-icon.ts
Normal file
35
src/main/tray/create-tray-icon.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { nativeImage } from "electron";
|
||||||
|
import type { NativeImage } from "electron";
|
||||||
|
import { base64, getOrInsertWithAsync } from "../../common/utils";
|
||||||
|
import sharp from "sharp";
|
||||||
|
import { JSDOM } from "jsdom";
|
||||||
|
|
||||||
|
export interface CreateTrayIconArgs {
|
||||||
|
shouldUseDarkColors: boolean;
|
||||||
|
size: number;
|
||||||
|
sourceSvg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trayIcons = new Map<boolean, NativeImage>();
|
||||||
|
|
||||||
|
export async function createTrayIcon({ shouldUseDarkColors, size, sourceSvg }: CreateTrayIconArgs): Promise<NativeImage> {
|
||||||
|
return getOrInsertWithAsync(trayIcons, shouldUseDarkColors, async () => {
|
||||||
|
const trayIconColor = shouldUseDarkColors ? "white" : "black"; // Invert to show contrast
|
||||||
|
const parsedSvg = base64.decode(sourceSvg.split("base64,")[1]);
|
||||||
|
const svgDom = new JSDOM(`<body>${parsedSvg}</body>`);
|
||||||
|
const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0];
|
||||||
|
|
||||||
|
svgRoot.innerHTML += `<style>* {fill: ${trayIconColor} !important;}</style>`;
|
||||||
|
|
||||||
|
const iconBuffer = await sharp(Buffer.from(svgRoot.outerHTML))
|
||||||
|
.resize({ width: size, height: size })
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
return nativeImage.createFromBuffer(iconBuffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
130
src/main/tray/init-tray.injectable.ts
Normal file
130
src/main/tray/init-tray.injectable.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import packageInfo from "../../../package.json";
|
||||||
|
import { Menu, Tray } from "electron";
|
||||||
|
import { autorun } from "mobx";
|
||||||
|
import { showAbout } from "../menu/menu";
|
||||||
|
import { checkForUpdates, isAutoUpdateEnabled } from "../app-updater";
|
||||||
|
import type { WindowManager } from "../window-manager";
|
||||||
|
import logger from "../logger";
|
||||||
|
import { isWindows, productName } from "../../common/vars";
|
||||||
|
import { exitApp } from "../exit-app";
|
||||||
|
import type { Disposer } from "../../common/utils";
|
||||||
|
import { disposer, toJS } from "../../common/utils";
|
||||||
|
import type { TrayMenuRegistration } from "./tray-menu-registration";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import windowManagerInjectable from "../window-manager.injectable";
|
||||||
|
import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
||||||
|
import trayMenuItemsInjectable from "./tray-menu-items.injectable";
|
||||||
|
import createCurrentTrayIconInjectable from "./create-current-tray-icon.injectable";
|
||||||
|
import trayIconUpdaterInjectable from "./tray-icon-updater.injectable";
|
||||||
|
|
||||||
|
const initTrayInjectable = getInjectable({
|
||||||
|
id: "init-tray",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const windowManager = di.inject(windowManagerInjectable);
|
||||||
|
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
||||||
|
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||||
|
const createCurrentTrayIcon = di.inject(createCurrentTrayIconInjectable);
|
||||||
|
const trayIconUpdater = di.inject(trayIconUpdaterInjectable);
|
||||||
|
|
||||||
|
return async (): Promise<Disposer> => {
|
||||||
|
const tray = new Tray(await createCurrentTrayIcon());
|
||||||
|
|
||||||
|
tray.setToolTip(packageInfo.description);
|
||||||
|
tray.setIgnoreDoubleClickEvents(true);
|
||||||
|
|
||||||
|
if (isWindows) {
|
||||||
|
tray.on("click", () => {
|
||||||
|
windowManager
|
||||||
|
.ensureMainWindow()
|
||||||
|
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposer(
|
||||||
|
trayIconUpdater(tray),
|
||||||
|
autorun(() => {
|
||||||
|
try {
|
||||||
|
const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get()), navigateToPreferences);
|
||||||
|
|
||||||
|
tray.setContextMenu(menu);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`${TRAY_LOG_PREFIX}: building failed`, { error });
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
() => tray.destroy(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default initTrayInjectable;
|
||||||
|
|
||||||
|
const TRAY_LOG_PREFIX = "[TRAY]";
|
||||||
|
|
||||||
|
function getMenuItemConstructorOptions(trayItem: TrayMenuRegistration): Electron.MenuItemConstructorOptions {
|
||||||
|
return {
|
||||||
|
...trayItem,
|
||||||
|
submenu: trayItem.submenu ? trayItem.submenu.map(getMenuItemConstructorOptions) : undefined,
|
||||||
|
click: trayItem.click ? () => {
|
||||||
|
trayItem.click(trayItem);
|
||||||
|
} : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTrayMenu(
|
||||||
|
windowManager: WindowManager,
|
||||||
|
extensionTrayItems: TrayMenuRegistration[],
|
||||||
|
navigateToPreferences: () => void,
|
||||||
|
): Menu {
|
||||||
|
let template: Electron.MenuItemConstructorOptions[] = [
|
||||||
|
{
|
||||||
|
label: `Open ${productName}`,
|
||||||
|
click() {
|
||||||
|
windowManager
|
||||||
|
.ensureMainWindow()
|
||||||
|
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Preferences",
|
||||||
|
click() {
|
||||||
|
navigateToPreferences();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isAutoUpdateEnabled()) {
|
||||||
|
template.push({
|
||||||
|
label: "Check for updates",
|
||||||
|
click() {
|
||||||
|
checkForUpdates()
|
||||||
|
.then(() => windowManager.ensureMainWindow());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template = template.concat(extensionTrayItems.map(getMenuItemConstructorOptions));
|
||||||
|
|
||||||
|
return Menu.buildFromTemplate(template.concat([
|
||||||
|
{
|
||||||
|
label: `About ${productName}`,
|
||||||
|
click() {
|
||||||
|
windowManager.ensureMainWindow()
|
||||||
|
.then(showAbout)
|
||||||
|
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to show Lens About view`, { error }));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ type: "separator" },
|
||||||
|
{
|
||||||
|
label: "Quit App",
|
||||||
|
click() {
|
||||||
|
exitApp();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]));
|
||||||
|
}
|
||||||
44
src/main/tray/tray-icon-updater.injectable.ts
Normal file
44
src/main/tray/tray-icon-updater.injectable.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Tray } from "electron";
|
||||||
|
import type { Disposer } from "../../common/utils";
|
||||||
|
import nativeThemeInjectable from "../electron/native-theme.injectable";
|
||||||
|
import createCurrentTrayIconInjectable from "./create-current-tray-icon.injectable";
|
||||||
|
|
||||||
|
export type TrayIconUpdater = (tray: Tray) => Disposer;
|
||||||
|
|
||||||
|
const trayIconUpdaterInjectable = getInjectable({
|
||||||
|
id: "tray-icon-updater",
|
||||||
|
instantiate: (di): TrayIconUpdater => {
|
||||||
|
const nativeTheme = di.inject(nativeThemeInjectable);
|
||||||
|
const createCurrentTrayIcon = di.inject(createCurrentTrayIconInjectable);
|
||||||
|
|
||||||
|
return (tray) => {
|
||||||
|
let prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
||||||
|
const onUpdated = () => {
|
||||||
|
if (prevShouldUseDarkColors !== nativeTheme.shouldUseDarkColors) {
|
||||||
|
const localShouldUseDarkColors = prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
||||||
|
|
||||||
|
createCurrentTrayIcon()
|
||||||
|
.then(img => {
|
||||||
|
// This guards against rapid changes back and forth
|
||||||
|
if (localShouldUseDarkColors === prevShouldUseDarkColors) {
|
||||||
|
tray.setImage(img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeTheme.on("updated", onUpdated);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
nativeTheme.off("updated", onUpdated);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default trayIconUpdaterInjectable;
|
||||||
@ -1,181 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import packageInfo from "../../../package.json";
|
|
||||||
import type { NativeImage } from "electron";
|
|
||||||
import { Menu, nativeImage, nativeTheme, Tray } from "electron";
|
|
||||||
import type { IComputedValue } from "mobx";
|
|
||||||
import { autorun } from "mobx";
|
|
||||||
import { showAbout } from "../menu/menu";
|
|
||||||
import { checkForUpdates, isAutoUpdateEnabled } from "../app-updater";
|
|
||||||
import type { WindowManager } from "../window-manager";
|
|
||||||
import logger from "../logger";
|
|
||||||
import { isWindows, productName } from "../../common/vars";
|
|
||||||
import { exitApp } from "../exit-app";
|
|
||||||
import type { Disposer } from "../../common/utils";
|
|
||||||
import { base64, disposer, getOrInsertWithAsync, toJS } from "../../common/utils";
|
|
||||||
import type { TrayMenuRegistration } from "./tray-menu-registration";
|
|
||||||
import sharp from "sharp";
|
|
||||||
import LogoLens from "../../renderer/components/icon/logo-lens.svg";
|
|
||||||
import { JSDOM } from "jsdom";
|
|
||||||
|
|
||||||
|
|
||||||
const TRAY_LOG_PREFIX = "[TRAY]";
|
|
||||||
|
|
||||||
// note: instance of Tray should be saved somewhere, otherwise it disappears
|
|
||||||
export let tray: Tray;
|
|
||||||
|
|
||||||
interface CreateTrayIconArgs {
|
|
||||||
shouldUseDarkColors: boolean;
|
|
||||||
size: number;
|
|
||||||
sourceSvg: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const trayIcons = new Map<boolean, NativeImage>();
|
|
||||||
|
|
||||||
async function createTrayIcon({ shouldUseDarkColors, size, sourceSvg }: CreateTrayIconArgs): Promise<NativeImage> {
|
|
||||||
return getOrInsertWithAsync(trayIcons, shouldUseDarkColors, async () => {
|
|
||||||
const trayIconColor = shouldUseDarkColors ? "white" : "black"; // Invert to show contrast
|
|
||||||
const parsedSvg = base64.decode(sourceSvg.split("base64,")[1]);
|
|
||||||
const svgDom = new JSDOM(`<body>${parsedSvg}</body>`);
|
|
||||||
const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0];
|
|
||||||
|
|
||||||
svgRoot.innerHTML += `<style>* {fill: ${trayIconColor} !important;}</style>`;
|
|
||||||
|
|
||||||
const iconBuffer = await sharp(Buffer.from(svgRoot.outerHTML))
|
|
||||||
.resize({ width: size, height: size })
|
|
||||||
.png()
|
|
||||||
.toBuffer();
|
|
||||||
|
|
||||||
return nativeImage.createFromBuffer(iconBuffer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCurrentTrayIcon() {
|
|
||||||
return createTrayIcon({
|
|
||||||
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
|
|
||||||
size: 16,
|
|
||||||
sourceSvg: LogoLens,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function watchShouldUseDarkColors(tray: Tray): Disposer {
|
|
||||||
let prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
|
||||||
const onUpdated = () => {
|
|
||||||
if (prevShouldUseDarkColors !== nativeTheme.shouldUseDarkColors) {
|
|
||||||
prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
|
||||||
createCurrentTrayIcon()
|
|
||||||
.then(img => tray.setImage(img));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
nativeTheme.on("updated", onUpdated);
|
|
||||||
|
|
||||||
return () => nativeTheme.off("updated", onUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function initTray(
|
|
||||||
windowManager: WindowManager,
|
|
||||||
trayMenuItems: IComputedValue<TrayMenuRegistration[]>,
|
|
||||||
navigateToPreferences: () => void,
|
|
||||||
): Promise<Disposer> {
|
|
||||||
const icon = await createCurrentTrayIcon();
|
|
||||||
const dispose = disposer();
|
|
||||||
|
|
||||||
tray = new Tray(icon);
|
|
||||||
tray.setToolTip(packageInfo.description);
|
|
||||||
tray.setIgnoreDoubleClickEvents(true);
|
|
||||||
|
|
||||||
dispose.push(watchShouldUseDarkColors(tray));
|
|
||||||
|
|
||||||
if (isWindows) {
|
|
||||||
tray.on("click", () => {
|
|
||||||
windowManager
|
|
||||||
.ensureMainWindow()
|
|
||||||
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose.push(
|
|
||||||
autorun(() => {
|
|
||||||
try {
|
|
||||||
const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get()), navigateToPreferences);
|
|
||||||
|
|
||||||
tray.setContextMenu(menu);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`${TRAY_LOG_PREFIX}: building failed`, { error });
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
() => {
|
|
||||||
tray?.destroy();
|
|
||||||
tray = null;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return dispose;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMenuItemConstructorOptions(trayItem: TrayMenuRegistration): Electron.MenuItemConstructorOptions {
|
|
||||||
return {
|
|
||||||
...trayItem,
|
|
||||||
submenu: trayItem.submenu ? trayItem.submenu.map(getMenuItemConstructorOptions) : undefined,
|
|
||||||
click: trayItem.click ? () => {
|
|
||||||
trayItem.click(trayItem);
|
|
||||||
} : undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTrayMenu(
|
|
||||||
windowManager: WindowManager,
|
|
||||||
extensionTrayItems: TrayMenuRegistration[],
|
|
||||||
navigateToPreferences: () => void,
|
|
||||||
): Menu {
|
|
||||||
let template: Electron.MenuItemConstructorOptions[] = [
|
|
||||||
{
|
|
||||||
label: `Open ${productName}`,
|
|
||||||
click() {
|
|
||||||
windowManager
|
|
||||||
.ensureMainWindow()
|
|
||||||
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Preferences",
|
|
||||||
click() {
|
|
||||||
navigateToPreferences();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isAutoUpdateEnabled()) {
|
|
||||||
template.push({
|
|
||||||
label: "Check for updates",
|
|
||||||
click() {
|
|
||||||
checkForUpdates()
|
|
||||||
.then(() => windowManager.ensureMainWindow());
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template = template.concat(extensionTrayItems.map(getMenuItemConstructorOptions));
|
|
||||||
|
|
||||||
return Menu.buildFromTemplate(template.concat([
|
|
||||||
{
|
|
||||||
label: `About ${productName}`,
|
|
||||||
click() {
|
|
||||||
windowManager.ensureMainWindow()
|
|
||||||
.then(showAbout)
|
|
||||||
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to show Lens About view`, { error }));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ type: "separator" },
|
|
||||||
{
|
|
||||||
label: "Quit App",
|
|
||||||
click() {
|
|
||||||
exitApp();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user