mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make adding of new tray icons easier by complying to Open Closed Principle
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
47a919e499
commit
23cbc2a2af
@ -6,11 +6,11 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-item-injection-token";
|
import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-item-injection-token";
|
||||||
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||||
import updateIsBeingDownloadedInjectable from "../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
|
|
||||||
import { withErrorSuppression } from "../../common/utils/with-error-suppression/with-error-suppression";
|
import { withErrorSuppression } from "../../common/utils/with-error-suppression/with-error-suppression";
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import withErrorLoggingInjectable from "../../common/utils/with-error-logging/with-error-logging.injectable";
|
import withErrorLoggingInjectable from "../../common/utils/with-error-logging/with-error-logging.injectable";
|
||||||
import quitAndInstallUpdateInjectable from "./quit-and-install-update.injectable";
|
import quitAndInstallUpdateInjectable from "./quit-and-install-update.injectable";
|
||||||
|
import updateIsReadyToBeInstalledInjectable from "./update-is-ready-to-be-installed.injectable";
|
||||||
|
|
||||||
const installApplicationUpdateTrayItemInjectable = getInjectable({
|
const installApplicationUpdateTrayItemInjectable = getInjectable({
|
||||||
id: "install-update-tray-item",
|
id: "install-update-tray-item",
|
||||||
@ -18,8 +18,8 @@ const installApplicationUpdateTrayItemInjectable = getInjectable({
|
|||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const quitAndInstallUpdate = di.inject(quitAndInstallUpdateInjectable);
|
const quitAndInstallUpdate = di.inject(quitAndInstallUpdateInjectable);
|
||||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||||
const downloadingUpdateState = di.inject(updateIsBeingDownloadedInjectable);
|
|
||||||
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||||
|
const updateIsReadyToBeInstalled = di.inject(updateIsReadyToBeInstalledInjectable);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: "install-update",
|
id: "install-update",
|
||||||
@ -34,9 +34,7 @@ const installApplicationUpdateTrayItemInjectable = getInjectable({
|
|||||||
|
|
||||||
enabled: computed(() => true),
|
enabled: computed(() => true),
|
||||||
|
|
||||||
visible: computed(
|
visible: updateIsReadyToBeInstalled,
|
||||||
() => !!discoveredVersionState.value.get() && !downloadingUpdateState.value.get(),
|
|
||||||
),
|
|
||||||
|
|
||||||
click: pipeline(
|
click: pipeline(
|
||||||
quitAndInstallUpdate,
|
quitAndInstallUpdate,
|
||||||
|
|||||||
@ -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 { computed } from "mobx";
|
||||||
|
import getTrayIconPathInjectable from "../../tray/menu-icon/get-tray-icon-path.injectable";
|
||||||
|
import { trayIconInjectionToken } from "../../tray/menu-icon/tray-icon-injection-token";
|
||||||
|
import updateIsReadyToBeInstalledInjectable from "../update-is-ready-to-be-installed.injectable";
|
||||||
|
|
||||||
|
const updateIsReadyToBeInstalledTrayIconInjectable = getInjectable({
|
||||||
|
id: "update-is-ready-to-be-installed-tray-icon",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const getTrayIconPath = di.inject(getTrayIconPathInjectable);
|
||||||
|
const updateIsReadyToBeInstalled = di.inject(updateIsReadyToBeInstalledInjectable);
|
||||||
|
|
||||||
|
return {
|
||||||
|
iconPath: getTrayIconPath("update-available"),
|
||||||
|
priority: 1,
|
||||||
|
shouldBeShown: computed(() => updateIsReadyToBeInstalled.get()),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: trayIconInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default updateIsReadyToBeInstalledTrayIconInjectable;
|
||||||
@ -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 { computed } from "mobx";
|
||||||
|
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||||
|
import updateIsBeingDownloadedInjectable from "../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
|
||||||
|
|
||||||
|
const updateIsReadyToBeInstalledInjectable = getInjectable({
|
||||||
|
id: "update-is-ready-to-be-installed",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const discoveredUpdateVersion = di.inject(discoveredUpdateVersionInjectable);
|
||||||
|
const updateIsBeingDownloaded = di.inject(updateIsBeingDownloadedInjectable);
|
||||||
|
|
||||||
|
return computed(
|
||||||
|
() =>
|
||||||
|
!!discoveredUpdateVersion.value.get() &&
|
||||||
|
!updateIsBeingDownloaded.value.get(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default updateIsReadyToBeInstalledInjectable;
|
||||||
@ -8,8 +8,8 @@ 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 { convertToElectronMenuTemplate } from "../reactive-tray-menu-items/converters";
|
import { convertToElectronMenuTemplate } from "../reactive-tray-menu-items/converters";
|
||||||
|
import trayIconInjectable from "../menu-icon/tray-icon.injectable";
|
||||||
|
|
||||||
const TRAY_LOG_PREFIX = "[TRAY]";
|
const TRAY_LOG_PREFIX = "[TRAY]";
|
||||||
|
|
||||||
@ -38,13 +38,13 @@ const electronTrayInjectable = getInjectable({
|
|||||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||||
const isWindows = di.inject(isWindowsInjectable);
|
const isWindows = di.inject(isWindowsInjectable);
|
||||||
const logger = di.inject(loggerInjectable);
|
const logger = di.inject(loggerInjectable);
|
||||||
const trayIconPaths = di.inject(trayIconPathsInjectable);
|
const trayIcon = di.inject(trayIconInjectable);
|
||||||
|
|
||||||
let tray: Tray;
|
let tray: Tray;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start: () => {
|
start: () => {
|
||||||
tray = new Tray(trayIconPaths.normal);
|
tray = new Tray(trayIcon.get().iconPath);
|
||||||
|
|
||||||
tray.setToolTip(packageJson.description);
|
tray.setToolTip(packageJson.description);
|
||||||
tray.setIgnoreDoubleClickEvents(true);
|
tray.setIgnoreDoubleClickEvents(true);
|
||||||
|
|||||||
37
src/main/tray/menu-icon/get-tray-icon-path.injectable.ts
Normal file
37
src/main/tray/menu-icon/get-tray-icon-path.injectable.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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 getAbsolutePathInjectable from "../../../common/path/get-absolute-path.injectable";
|
||||||
|
import staticFilesDirectoryInjectable from "../../../common/vars/static-files-directory.injectable";
|
||||||
|
import isDevelopmentInjectable from "../../../common/vars/is-development.injectable";
|
||||||
|
import isMacInjectable from "../../../common/vars/is-mac.injectable";
|
||||||
|
import { camelCase, flow, upperFirst } from "lodash/fp";
|
||||||
|
const upperCamelCase = flow(camelCase, upperFirst);
|
||||||
|
|
||||||
|
const getTrayIconPathInjectable = getInjectable({
|
||||||
|
id: "get-tray-icon-path",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
||||||
|
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
|
||||||
|
const isDevelopment = di.inject(isDevelopmentInjectable);
|
||||||
|
const isMac = di.inject(isMacInjectable);
|
||||||
|
|
||||||
|
const baseIconDirectory = getAbsolutePath(
|
||||||
|
staticFilesDirectory,
|
||||||
|
isDevelopment ? "../build/tray" : "icons", // copied within electron-builder extras
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileSuffix = isMac ? "Template.png" : ".png";
|
||||||
|
|
||||||
|
return (name: string) =>
|
||||||
|
getAbsolutePath(
|
||||||
|
baseIconDirectory,
|
||||||
|
`trayIcon${upperCamelCase(name)}${fileSuffix}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getTrayIconPathInjectable;
|
||||||
26
src/main/tray/menu-icon/normal-tray-icon.injectable.ts
Normal file
26
src/main/tray/menu-icon/normal-tray-icon.injectable.ts
Normal file
@ -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;
|
||||||
@ -4,27 +4,22 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { reaction } from "mobx";
|
import { reaction } from "mobx";
|
||||||
import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
|
||||||
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||||
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
|
import electronTrayInjectable from "../electron-tray/electron-tray.injectable";
|
||||||
import trayIconPathsInjectable from "../tray-icon-path.injectable";
|
import trayIconInjectable from "./tray-icon.injectable";
|
||||||
|
|
||||||
const reactiveTrayMenuIconInjectable = getInjectable({
|
const reactiveTrayMenuIconInjectable = getInjectable({
|
||||||
id: "reactive-tray-menu-icon",
|
id: "reactive-tray-menu-icon",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const discoveredUpdateVersion = di.inject(discoveredUpdateVersionInjectable);
|
const trayMenuIcon = di.inject(trayIconInjectable);
|
||||||
const electronTray = di.inject(electronTrayInjectable);
|
const electronTray = di.inject(electronTrayInjectable);
|
||||||
const trayIconPaths = di.inject(trayIconPathsInjectable);
|
|
||||||
|
|
||||||
return getStartableStoppable("reactive-tray-menu-icon", () => (
|
return getStartableStoppable("reactive-tray-menu-icon", () => (
|
||||||
reaction(
|
reaction(
|
||||||
() => discoveredUpdateVersion.value.get(),
|
() => trayMenuIcon.get(),
|
||||||
updateVersion => {
|
icon => {
|
||||||
if (updateVersion) {
|
electronTray.setIconPath(icon.iconPath);
|
||||||
electronTray.setIconPath(trayIconPaths.updateAvailable);
|
|
||||||
} else {
|
|
||||||
electronTray.setIconPath(trayIconPaths.normal);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
|
|||||||
16
src/main/tray/menu-icon/tray-icon-injection-token.ts
Normal file
16
src/main/tray/menu-icon/tray-icon-injection-token.ts
Normal file
@ -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",
|
||||||
|
});
|
||||||
33
src/main/tray/menu-icon/tray-icon.injectable.ts
Normal file
33
src/main/tray/menu-icon/tray-icon.injectable.ts
Normal file
@ -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;
|
||||||
@ -1,37 +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 getAbsolutePathInjectable from "../../common/path/get-absolute-path.injectable";
|
|
||||||
import staticFilesDirectoryInjectable from "../../common/vars/static-files-directory.injectable";
|
|
||||||
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
|
|
||||||
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
|
||||||
|
|
||||||
export interface TrayIconPaths {
|
|
||||||
normal: string;
|
|
||||||
updateAvailable: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const trayIconPathsInjectable = getInjectable({
|
|
||||||
id: "tray-icon-paths",
|
|
||||||
|
|
||||||
instantiate: (di): TrayIconPaths => {
|
|
||||||
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
|
||||||
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
|
|
||||||
const isDevelopment = di.inject(isDevelopmentInjectable);
|
|
||||||
const isMac = di.inject(isMacInjectable);
|
|
||||||
const baseIconDirectory = getAbsolutePath(
|
|
||||||
staticFilesDirectory,
|
|
||||||
isDevelopment ? "../build/tray" : "icons", // copied within electron-builder extras
|
|
||||||
);
|
|
||||||
const fileSuffix = isMac ? "Template.png" : ".png";
|
|
||||||
|
|
||||||
return {
|
|
||||||
normal: getAbsolutePath(baseIconDirectory, `trayIcon${fileSuffix}`),
|
|
||||||
updateAvailable: getAbsolutePath(baseIconDirectory, `trayIconUpdateAvailable${fileSuffix}`),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default trayIconPathsInjectable;
|
|
||||||
@ -43,7 +43,6 @@ import applicationWindowInjectable from "../../../main/start-main-application/le
|
|||||||
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 trayIconPathsInjectable from "../../../main/tray/tray-icon-path.injectable";
|
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { openMenu } from "react-select-event";
|
import { openMenu } from "react-select-event";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
@ -194,11 +193,7 @@ export const getApplicationBuilder = () => {
|
|||||||
const traySetMenuItemsMock = jest.fn<any, [MinimalTrayMenuItem[]]>();
|
const traySetMenuItemsMock = jest.fn<any, [MinimalTrayMenuItem[]]>();
|
||||||
|
|
||||||
mainDi.override(electronTrayInjectable, () => ({
|
mainDi.override(electronTrayInjectable, () => ({
|
||||||
start: () => {
|
start: () => {},
|
||||||
const iconPaths = mainDi.inject(trayIconPathsInjectable);
|
|
||||||
|
|
||||||
trayMenuIconPath = iconPaths.normal;
|
|
||||||
},
|
|
||||||
stop: () => {},
|
stop: () => {},
|
||||||
setMenuItems: traySetMenuItemsMock,
|
setMenuItems: traySetMenuItemsMock,
|
||||||
setIconPath: (path) => {
|
setIconPath: (path) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user