mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add notifications about change in update status
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
975109b75c
commit
a54d366a24
@ -23,6 +23,7 @@ import showApplicationWindowInjectable from "../../main/start-main-application/l
|
||||
import type { AskBoolean } from "../../main/ask-boolean/ask-boolean.injectable";
|
||||
import askBooleanInjectable from "../../main/ask-boolean/ask-boolean.injectable";
|
||||
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||
import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable";
|
||||
|
||||
describe("installing update using tray", () => {
|
||||
let applicationBuilder: ApplicationBuilder;
|
||||
@ -31,21 +32,23 @@ describe("installing update using tray", () => {
|
||||
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
|
||||
let setUpdateOnQuitMock: jest.Mock;
|
||||
let showApplicationWindowMock: jest.Mock;
|
||||
let showNotificationMock: jest.Mock;
|
||||
let showInfoNotificationMock: jest.Mock;
|
||||
let askBooleanMock: AsyncFnMock<AskBoolean>;
|
||||
|
||||
beforeEach(() => {
|
||||
applicationBuilder = getApplicationBuilder();
|
||||
|
||||
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||
quitAndInstallUpdateMock = jest.fn();
|
||||
checkForPlatformUpdatesMock = asyncFn();
|
||||
downloadPlatformUpdateMock = asyncFn();
|
||||
setUpdateOnQuitMock = jest.fn();
|
||||
showApplicationWindowMock = jest.fn();
|
||||
showNotificationMock = jest.fn(() => () => {});
|
||||
showInfoNotificationMock = jest.fn(() => () => {});
|
||||
askBooleanMock = asyncFn();
|
||||
|
||||
rendererDi.override(showInfoNotificationInjectable, () => showInfoNotificationMock);
|
||||
|
||||
mainDi.override(askBooleanInjectable, () => askBooleanMock);
|
||||
mainDi.override(showApplicationWindowInjectable, () => showApplicationWindowMock);
|
||||
mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock);
|
||||
@ -103,8 +106,8 @@ describe("installing update using tray", () => {
|
||||
expect(showApplicationWindowMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
xit("notifies the user that checking for updates is happening", () => {
|
||||
expect(showNotificationMock).toHaveBeenCalledWith("Checking for updates...");
|
||||
it("notifies the user that checking for updates is happening", () => {
|
||||
expect(showInfoNotificationMock).toHaveBeenCalledWith("Checking for updates...");
|
||||
});
|
||||
|
||||
it("user cannot check for updates again", () => {
|
||||
@ -129,7 +132,7 @@ describe("installing update using tray", () => {
|
||||
|
||||
describe("when no new update is discovered", () => {
|
||||
beforeEach(async () => {
|
||||
showNotificationMock.mockClear();
|
||||
showInfoNotificationMock.mockClear();
|
||||
|
||||
await checkForPlatformUpdatesMock.resolve({
|
||||
updateWasDiscovered: false,
|
||||
@ -142,8 +145,8 @@ describe("installing update using tray", () => {
|
||||
expect(showApplicationWindowMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
xit("notifies the user", () => {
|
||||
expect(showNotificationMock).toHaveBeenCalledWith("No new updates available");
|
||||
it("notifies the user", () => {
|
||||
expect(showInfoNotificationMock).toHaveBeenCalledWith("No new updates available");
|
||||
});
|
||||
|
||||
it("does not start downloading update", () => {
|
||||
@ -189,8 +192,8 @@ describe("installing update using tray", () => {
|
||||
expect(downloadPlatformUpdateMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
xit("notifies the user that download is happening", () => {
|
||||
expect(showNotificationMock).toHaveBeenCalledWith("Download for version some-version started...");
|
||||
it("notifies the user that download is happening", () => {
|
||||
expect(showInfoNotificationMock).toHaveBeenCalledWith("Download for version some-version started...");
|
||||
});
|
||||
|
||||
it("user cannot check for updates again yet", () => {
|
||||
@ -246,8 +249,8 @@ describe("installing update using tray", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
xit("notifies the user about failed download", () => {
|
||||
expect(showNotificationMock).toHaveBeenCalledWith("Failed to download update");
|
||||
it("notifies the user about failed download", () => {
|
||||
expect(showInfoNotificationMock).toHaveBeenCalledWith("Download of update failed");
|
||||
});
|
||||
|
||||
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
||||
@ -400,6 +403,7 @@ describe("installing update using tray", () => {
|
||||
|
||||
await checkForPlatformUpdatesMock.resolve({
|
||||
updateWasDiscovered: true,
|
||||
version: "some-version",
|
||||
});
|
||||
|
||||
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
|
||||
@ -425,6 +429,7 @@ describe("installing update using tray", () => {
|
||||
|
||||
await checkForPlatformUpdatesMock.resolve({
|
||||
updateWasDiscovered: true,
|
||||
version: "some-version",
|
||||
});
|
||||
|
||||
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
|
||||
@ -450,6 +455,7 @@ describe("installing update using tray", () => {
|
||||
|
||||
await checkForPlatformUpdatesMock.resolve({
|
||||
updateWasDiscovered: true,
|
||||
version: "some-version",
|
||||
});
|
||||
|
||||
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
|
||||
|
||||
@ -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 type { Channel } from "../channel/channel-injection-token";
|
||||
import { channelInjectionToken } from "../channel/channel-injection-token";
|
||||
|
||||
export type ApplicationUpdateStatusEventId =
|
||||
| "checking-for-updates"
|
||||
| "no-updates-available"
|
||||
| "download-for-update-started"
|
||||
| "download-for-update-failed";
|
||||
|
||||
export type ApplicationUpdateStatusChannel = Channel<{ eventId: ApplicationUpdateStatusEventId; version?: string }>;
|
||||
|
||||
const applicationUpdateStatusChannelInjectable = getInjectable({
|
||||
id: "application-update-status-channel",
|
||||
|
||||
instantiate: (): ApplicationUpdateStatusChannel => ({
|
||||
id: "application-update-status-channel",
|
||||
}),
|
||||
|
||||
injectionToken: channelInjectionToken,
|
||||
});
|
||||
|
||||
export default applicationUpdateStatusChannelInjectable;
|
||||
@ -12,7 +12,7 @@ const discoveredUpdateVersionInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
const createSyncBox = di.inject(createSyncBoxInjectable);
|
||||
|
||||
return createSyncBox<{ version: string; updateChannel: UpdateChannel }>(
|
||||
return createSyncBox<{ version: string; updateChannel: UpdateChannel } | null>(
|
||||
"discovered-update-version",
|
||||
);
|
||||
},
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { createChannel } from "../ipc-channel/create-channel/create-channel";
|
||||
|
||||
export const notificationChannel = createChannel<string>("notification:message");
|
||||
@ -1,21 +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 applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable";
|
||||
import { notificationChannel } from "../../common/notification/notification-channel";
|
||||
|
||||
const showNotificationInjectable = getInjectable({
|
||||
id: "show-notification",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationWindow = di.inject(applicationWindowInjectable);
|
||||
|
||||
return (message: string) => {
|
||||
applicationWindow.send({ channel: notificationChannel.name, data: [message] });
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default showNotificationInjectable;
|
||||
@ -7,7 +7,6 @@ import { computed } from "mobx";
|
||||
import updatingIsEnabledInjectable from "./updating-is-enabled.injectable";
|
||||
import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-item-injection-token";
|
||||
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
|
||||
import showNotificationInjectable from "../show-notification/show-notification.injectable";
|
||||
import askBooleanInjectable from "../ask-boolean/ask-boolean.injectable";
|
||||
import quitAndInstallUpdateInjectable from "../electron-app/features/quit-and-install-update.injectable";
|
||||
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
@ -16,6 +15,9 @@ import updatesAreBeingDiscoveredInjectable from "../../common/application-update
|
||||
import checkForUpdatesInjectable from "./check-for-updates/check-for-updates.injectable";
|
||||
import downloadUpdateInjectable from "./download-update/download-update.injectable";
|
||||
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||
import assert from "assert";
|
||||
import { sendToAgnosticChannelInjectionToken } from "../../common/channel/send-to-agnostic-channel-injection-token";
|
||||
import applicationUpdateStatusChannelInjectable from "../../common/application-update/application-update-status-channel.injectable";
|
||||
|
||||
const checkForUpdatesTrayItemInjectable = getInjectable({
|
||||
id: "check-for-updates-tray-item",
|
||||
@ -24,7 +26,6 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
|
||||
const progressOfUpdateDownload = di.inject(progressOfUpdateDownloadInjectable);
|
||||
const showNotification = di.inject(showNotificationInjectable);
|
||||
const askBoolean = di.inject(askBooleanInjectable);
|
||||
const quitAndInstallUpdate = di.inject(quitAndInstallUpdateInjectable);
|
||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||
@ -32,6 +33,8 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
|
||||
const checkingForUpdatesState = di.inject(updatesAreBeingDiscoveredInjectable);
|
||||
const checkForUpdates = di.inject(checkForUpdatesInjectable);
|
||||
const downloadUpdate = di.inject(downloadUpdateInjectable);
|
||||
const sendToAgnosticChannel = di.inject(sendToAgnosticChannelInjectionToken);
|
||||
const applicationUpdateStatusChannel = di.inject(applicationUpdateStatusChannelInjectable);
|
||||
|
||||
return {
|
||||
id: "check-for-updates",
|
||||
@ -40,7 +43,11 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
|
||||
|
||||
label: computed(() => {
|
||||
if (downloadingUpdateState.value.get()) {
|
||||
return `Downloading update ${discoveredVersionState.value.get().version} (${progressOfUpdateDownload.value.get()}%)...`;
|
||||
const discoveredVersion = discoveredVersionState.value.get();
|
||||
|
||||
assert(discoveredVersion);
|
||||
|
||||
return `Downloading update ${discoveredVersion.version} (${progressOfUpdateDownload.value.get()}%)...`;
|
||||
}
|
||||
|
||||
if (checkingForUpdatesState.value.get()) {
|
||||
@ -58,13 +65,13 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
|
||||
const { updateWasDiscovered, version } = await checkForUpdates();
|
||||
|
||||
if (updateWasDiscovered) {
|
||||
showNotification(`Download for version ${version} started...`);
|
||||
sendToAgnosticChannel(applicationUpdateStatusChannel, { eventId: "download-for-update-started", version });
|
||||
|
||||
// Note: intentional orphan promise to make download happen in the background
|
||||
downloadUpdate().then(async ({ downloadWasSuccessful }) => {
|
||||
|
||||
if (!downloadWasSuccessful) {
|
||||
showNotification(`Download for update failed`);
|
||||
sendToAgnosticChannel(applicationUpdateStatusChannel, { eventId: "download-for-update-failed" });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7,17 +7,20 @@ import type { CheckForPlatformUpdates } from "../check-for-platform-updates/chec
|
||||
import checkForPlatformUpdatesInjectable from "../check-for-platform-updates/check-for-platform-updates.injectable";
|
||||
import type { UpdateChannel } from "../update-channels";
|
||||
import selectedUpdateChannelInjectable from "../selected-update-channel.injectable";
|
||||
import showNotificationInjectable from "../../show-notification/show-notification.injectable";
|
||||
import updatesAreBeingDiscoveredInjectable from "../../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable";
|
||||
import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
import { runInAction } from "mobx";
|
||||
import assert from "assert";
|
||||
import applicationUpdateStatusChannelInjectable from "../../../common/application-update/application-update-status-channel.injectable";
|
||||
import { sendToAgnosticChannelInjectionToken } from "../../../common/channel/send-to-agnostic-channel-injection-token";
|
||||
|
||||
const checkForUpdatesInjectable = getInjectable({
|
||||
id: "check-for-updates",
|
||||
|
||||
instantiate: (di) => {
|
||||
const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable);
|
||||
const showNotification = di.inject(showNotificationInjectable);
|
||||
const sendToAgnosticChannel = di.inject(sendToAgnosticChannelInjectionToken);
|
||||
const applicationUpdateStatusChannel = di.inject(applicationUpdateStatusChannelInjectable);
|
||||
|
||||
const checkForPlatformUpdates = di.inject(
|
||||
checkForPlatformUpdatesInjectable,
|
||||
@ -37,19 +40,24 @@ const checkForUpdatesInjectable = getInjectable({
|
||||
const checkForUpdatesStartingFromChannel =
|
||||
checkForUpdatesStartingFromChannelFor(checkForPlatformUpdates);
|
||||
|
||||
showNotification("Checking for updates...");
|
||||
sendToAgnosticChannel(applicationUpdateStatusChannel, { eventId: "checking-for-updates" });
|
||||
|
||||
const { updateWasDiscovered, version, actualUpdateChannel } =
|
||||
await checkForUpdatesStartingFromChannel(selectedUpdateChannel.value.get());
|
||||
|
||||
if (!updateWasDiscovered) {
|
||||
showNotification("No new updates available");
|
||||
sendToAgnosticChannel(applicationUpdateStatusChannel, { eventId: "no-updates-available" });
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
if (!updateWasDiscovered) {
|
||||
discoveredVersionState.set(null);
|
||||
} else {
|
||||
|
||||
// TODO: Unacceptable damage caused by strict mode
|
||||
assert(version);
|
||||
assert(actualUpdateChannel);
|
||||
|
||||
discoveredVersionState.set({
|
||||
version,
|
||||
updateChannel: actualUpdateChannel,
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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 { channelListenerInjectionToken } from "../../common/channel/channel-listener-injection-token";
|
||||
import type { ApplicationUpdateStatusEventId } from "../../common/application-update/application-update-status-channel.injectable";
|
||||
import applicationUpdateStatusChannelInjectable from "../../common/application-update/application-update-status-channel.injectable";
|
||||
import showInfoNotificationInjectable from "../components/notifications/show-info-notification.injectable";
|
||||
|
||||
const applicationUpdateStatusListenerInjectable = getInjectable({
|
||||
id: "application-update-status-listener",
|
||||
|
||||
instantiate: (di) => {
|
||||
const channel = di.inject(applicationUpdateStatusChannelInjectable);
|
||||
const showInfoNotification = di.inject(showInfoNotificationInjectable);
|
||||
|
||||
const eventHandlers: Record<ApplicationUpdateStatusEventId, { handle: (version: string) => void }> = {
|
||||
"checking-for-updates": {
|
||||
handle: () => {
|
||||
showInfoNotification("Checking for updates...");
|
||||
},
|
||||
},
|
||||
|
||||
"no-updates-available": {
|
||||
handle: () => {
|
||||
showInfoNotification("No new updates available");
|
||||
},
|
||||
},
|
||||
|
||||
"download-for-update-started": {
|
||||
handle: (version) => {
|
||||
showInfoNotification(`Download for version ${version} started...`);
|
||||
},
|
||||
},
|
||||
|
||||
"download-for-update-failed": {
|
||||
handle: () => {
|
||||
showInfoNotification("Download of update failed");
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
channel,
|
||||
|
||||
handler: ({ eventId, version }: { eventId: ApplicationUpdateStatusEventId; version: string }) => {
|
||||
eventHandlers[eventId].handle(version);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: channelListenerInjectionToken,
|
||||
});
|
||||
|
||||
export default applicationUpdateStatusListenerInjectable;
|
||||
@ -1,26 +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 { ipcChannelListenerInjectionToken } from "../../ipc-channel-listeners/ipc-channel-listener-injection-token";
|
||||
import { notificationChannel } from "../../../common/notification/notification-channel";
|
||||
import { Notifications } from "./index";
|
||||
|
||||
const notificationListenerInjectable = getInjectable({
|
||||
id: "notification-listener",
|
||||
|
||||
instantiate: () => ({
|
||||
channel: notificationChannel,
|
||||
|
||||
handle: (message: string) => {
|
||||
Notifications.shortInfo(message);
|
||||
},
|
||||
}),
|
||||
|
||||
causesSideEffects: true,
|
||||
|
||||
injectionToken: ipcChannelListenerInjectionToken,
|
||||
});
|
||||
|
||||
export default notificationListenerInjectable;
|
||||
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 { notificationsStore, NotificationStatus } from "./notifications.store";
|
||||
|
||||
const showInfoNotificationInjectable = getInjectable({
|
||||
id: "show-info-notification",
|
||||
|
||||
instantiate: () => (message: string) =>
|
||||
notificationsStore.add({
|
||||
status: NotificationStatus.INFO,
|
||||
timeout: 5000,
|
||||
message,
|
||||
}),
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default showInfoNotificationInjectable;
|
||||
@ -43,8 +43,6 @@ import type { IpcRenderer } from "electron";
|
||||
import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable";
|
||||
import { observable } from "mobx";
|
||||
import defaultShellInjectable from "./components/+preferences/default-shell.injectable";
|
||||
import notificationListenerInjectable from "./components/notifications/notification-listener.injectable";
|
||||
import { notificationChannel } from "../common/notification/notification-channel";
|
||||
|
||||
export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => {
|
||||
const {
|
||||
@ -121,8 +119,6 @@ export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => {
|
||||
di.override(getValueFromRegisteredChannelInjectable, () => () => Promise.resolve(undefined as never));
|
||||
di.override(registerIpcChannelListenerInjectable, () => () => undefined);
|
||||
|
||||
di.override(notificationListenerInjectable, () => ({ channel: notificationChannel, handle: () => {} }));
|
||||
|
||||
overrideFsWithFakes(di);
|
||||
|
||||
di.override(focusWindowInjectable, () => () => {});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user