Replace status bar item application update with different tray icon based on status of the update (#5769)

* Remove the auto-update status bar item for not being needed anymore

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make adding of new tray icons easier by complying to Open Closed Principle

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Start showing different tray icon when checking for updates

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Include placeholder icon for checking for updates

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Simplify code

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add first keyframe for downloading spinner

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Co-authored-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Janne SavolainenandSebastian Malton authored and GitHub committed 2022-07-05 15:44:03 -07:00
1 parent 26127fe906
commit 667053cdf5
52 files changed
+370 -1409

No files matched your search

@@ -6,11 +6,11 @@ import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
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 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 { pipeline } from "@ogre-tools/fp";
import withErrorLoggingInjectable from "../../common/utils/with-error-logging/with-error-logging.injectable";
import quitAndInstallUpdateInjectable from "./quit-and-install-update.injectable";
import updateIsReadyToBeInstalledInjectable from "./update-is-ready-to-be-installed.injectable";
const installApplicationUpdateTrayItemInjectable = getInjectable({
id: "install-update-tray-item",
@@ -18,8 +18,8 @@ const installApplicationUpdateTrayItemInjectable = getInjectable({
instantiate: (di) => {
const quitAndInstallUpdate = di.inject(quitAndInstallUpdateInjectable);
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
const downloadingUpdateState = di.inject(updateIsBeingDownloadedInjectable);
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
const updateIsReadyToBeInstalled = di.inject(updateIsReadyToBeInstalledInjectable);
return {
id: "install-update",
@@ -34,9 +34,7 @@ const installApplicationUpdateTrayItemInjectable = getInjectable({
enabled: computed(() => true),
visible: computed(
() => !!discoveredVersionState.value.get() && !downloadingUpdateState.value.get(),
),
visible: updateIsReadyToBeInstalled,
click: pipeline(
quitAndInstallUpdate,
@@ -0,0 +1,34 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { 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 updatesAreBeingDiscoveredInjectable from "../../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable";
import updateIsBeingDownloadedInjectable from "../../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
const checkingForUpdatesTrayIconInjectable = getInjectable({
id: "checking-for-updates-tray-icon",
instantiate: (di) => {
const getTrayIconPath = di.inject(getTrayIconPathInjectable);
const updatesAreBeingDiscovered = di.inject(updatesAreBeingDiscoveredInjectable);
const updateIsBeingDownloaded = di.inject(updateIsBeingDownloadedInjectable);
return {
iconPath: getTrayIconPath("checking-for-updates"),
priority: 1,
shouldBeShown: computed(
() =>
updatesAreBeingDiscovered.value.get() ||
updateIsBeingDownloaded.value.get(),
),
};
},
injectionToken: trayIconInjectionToken,
});
export default checkingForUpdatesTrayIconInjectable;
@@ -0,0 +1,27 @@
/**
* 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 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: updateIsReadyToBeInstalled,
};
},
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 isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import trayIconPathsInjectable from "../tray-icon-path.injectable";
import { convertToElectronMenuTemplate } from "../reactive-tray-menu-items/converters";
import trayIconInjectable from "../menu-icon/tray-icon.injectable";
const TRAY_LOG_PREFIX = "[TRAY]";
@@ -38,13 +38,13 @@ const electronTrayInjectable = getInjectable({
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const isWindows = di.inject(isWindowsInjectable);
const logger = di.inject(loggerInjectable);
const trayIconPaths = di.inject(trayIconPathsInjectable);
const trayIcon = di.inject(trayIconInjectable);
let tray: Tray;
return {
start: () => {
tray = new Tray(trayIconPaths.normal);
tray = new Tray(trayIcon.get().iconPath);
tray.setToolTip(packageJson.description);
tray.setIgnoreDoubleClickEvents(true);
@@ -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;
@@ -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;
+6 -11
View File
@@ -4,27 +4,22 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
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 electronTrayInjectable from "../electron-tray/electron-tray.injectable";
import trayIconPathsInjectable from "../tray-icon-path.injectable";
import trayIconInjectable from "./tray-icon.injectable";
const reactiveTrayMenuIconInjectable = getInjectable({
id: "reactive-tray-menu-icon",
instantiate: (di) => {
const discoveredUpdateVersion = di.inject(discoveredUpdateVersionInjectable);
const trayMenuIcon = di.inject(trayIconInjectable);
const electronTray = di.inject(electronTrayInjectable);
const trayIconPaths = di.inject(trayIconPathsInjectable);
return getStartableStoppable("reactive-tray-menu-icon", () => (
reaction(
() => discoveredUpdateVersion.value.get(),
updateVersion => {
if (updateVersion) {
electronTray.setIconPath(trayIconPaths.updateAvailable);
} else {
electronTray.setIconPath(trayIconPaths.normal);
}
() => trayMenuIcon.get(),
icon => {
electronTray.setIconPath(icon.iconPath);
},
{
fireImmediately: true,
@@ -0,0 +1,16 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx";
export interface TrayIcon {
iconPath: string;
priority: number;
shouldBeShown: IComputedValue<boolean>;
}
export const trayIconInjectionToken = getInjectionToken<TrayIcon>({
id: "tray-icon-token",
});
@@ -0,0 +1,33 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { pipeline } from "@ogre-tools/fp";
import { find, sortBy } from "lodash/fp";
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { trayIconInjectionToken } from "./tray-icon-injection-token";
const trayIconInjectable = getInjectable({
id: "tray-icon",
instantiate: (di) => {
const availableIcons = di.injectMany(trayIconInjectionToken);
return computed(() => {
const mostPrioritizedIcon = pipeline(
availableIcons,
sortBy((icon) => icon.priority),
find((icon) => icon.shouldBeShown.get()),
);
if (!mostPrioritizedIcon) {
throw new Error("There should always be tray icon which is shown.");
}
return mostPrioritizedIcon;
});
},
});
export default trayIconInjectable;
@@ -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;