mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add auto-update notifications and confirmation (#1941)
* add auto-update notifications and confirmation * Show single update notification (#1985) * Moving notification icons to top (#1987) * Switch to EventEmitter (producer&consumer) model * Add `onCorrect` and `onceCorrect` to ipc module for typechecking ipc messages * move type enforced ipc methods to seperate file, add unit tests Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
741973dd29
commit
a61425124f
23 files changed
+434
-47
No files matched your search
+68
-10
@@ -1,20 +1,78 @@
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import { autoUpdater, UpdateInfo } from "electron-updater";
|
||||
import logger from "./logger";
|
||||
import { isDevelopment, isTestEnv } from "../common/vars";
|
||||
import { delay } from "../common/utils";
|
||||
import { areArgsUpdateAvailableToBackchannel, AutoUpdateLogPrefix, broadcastMessage, onceCorrect, UpdateAvailableChannel, UpdateAvailableToBackchannel } from "../common/ipc";
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
export class AppUpdater {
|
||||
static readonly defaultUpdateIntervalMs = 1000 * 60 * 60 * 24; // once a day
|
||||
function handleAutoUpdateBackChannel(event: Electron.IpcMainEvent, ...[arg]: UpdateAvailableToBackchannel) {
|
||||
if (arg.doUpdate) {
|
||||
if (arg.now) {
|
||||
logger.info(`${AutoUpdateLogPrefix}: User chose to update now`);
|
||||
autoUpdater.downloadUpdate()
|
||||
.then(() => autoUpdater.quitAndInstall())
|
||||
.catch(error => logger.error(`${AutoUpdateLogPrefix}: Failed to download or install update`, { error }));
|
||||
} else {
|
||||
logger.info(`${AutoUpdateLogPrefix}: User chose to update on quit`);
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
autoUpdater.downloadUpdate()
|
||||
.catch(error => logger.error(`${AutoUpdateLogPrefix}: Failed to download update`, { error }));
|
||||
}
|
||||
} else {
|
||||
logger.info(`${AutoUpdateLogPrefix}: User chose not to update`);
|
||||
}
|
||||
}
|
||||
|
||||
static checkForUpdates() {
|
||||
return autoUpdater.checkForUpdatesAndNotify();
|
||||
/**
|
||||
* starts the automatic update checking
|
||||
* @param interval milliseconds between interval to check on, defaults to 24h
|
||||
*/
|
||||
export function startUpdateChecking(interval = 1000 * 60 * 60 * 24): void {
|
||||
if (isDevelopment || isTestEnv) {
|
||||
return;
|
||||
}
|
||||
|
||||
autoUpdater.logger = logger;
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.autoInstallOnAppQuit = false;
|
||||
|
||||
autoUpdater
|
||||
.on("update-available", (args: UpdateInfo) => {
|
||||
try {
|
||||
const backchannel = `auto-update:${args.version}`;
|
||||
|
||||
constructor(protected updateInterval = AppUpdater.defaultUpdateIntervalMs) {
|
||||
autoUpdater.logger = logger;
|
||||
ipcMain.removeAllListeners(backchannel); // only one handler should be present
|
||||
|
||||
// make sure that the handler is in place before broadcasting (prevent race-condition)
|
||||
onceCorrect({
|
||||
source: ipcMain,
|
||||
channel: backchannel,
|
||||
listener: handleAutoUpdateBackChannel,
|
||||
verifier: areArgsUpdateAvailableToBackchannel,
|
||||
});
|
||||
logger.info(`${AutoUpdateLogPrefix}: broadcasting update available`, { backchannel, version: args.version });
|
||||
broadcastMessage(UpdateAvailableChannel, backchannel, args);
|
||||
} catch (error) {
|
||||
logger.error(`${AutoUpdateLogPrefix}: broadcasting failed`, { error });
|
||||
}
|
||||
});
|
||||
|
||||
async function helper() {
|
||||
while (true) {
|
||||
await checkForUpdates();
|
||||
await delay(interval);
|
||||
}
|
||||
}
|
||||
|
||||
helper();
|
||||
}
|
||||
|
||||
public start() {
|
||||
setInterval(AppUpdater.checkForUpdates, this.updateInterval);
|
||||
export async function checkForUpdates(): Promise<void> {
|
||||
try {
|
||||
logger.info(`📡 Checking for app updates`);
|
||||
|
||||
return AppUpdater.checkForUpdates();
|
||||
await autoUpdater.checkForUpdates();
|
||||
} catch (error) {
|
||||
logger.error(`${AutoUpdateLogPrefix}: failed with an error`, { error: String(error) });
|
||||
}
|
||||
}
|
||||
+2
-6
@@ -10,7 +10,6 @@ import path from "path";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { ClusterManager } from "./cluster-manager";
|
||||
import { AppUpdater } from "./app-updater";
|
||||
import { shellSync } from "./shell-sync";
|
||||
import { getFreePort } from "./port";
|
||||
import { mangleProxyEnv } from "./proxy-env";
|
||||
@@ -28,6 +27,7 @@ import { installDeveloperTools } from "./developer-tools";
|
||||
import { filesystemProvisionerStore } from "./extension-filesystem";
|
||||
import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||
import { bindBroadcastHandlers } from "../common/ipc";
|
||||
import { startUpdateChecking } from "./app-updater";
|
||||
|
||||
const workingDir = path.join(app.getPath("appData"), appName);
|
||||
let proxyPort: number;
|
||||
@@ -72,11 +72,6 @@ app.on("ready", async () => {
|
||||
app.exit();
|
||||
});
|
||||
|
||||
logger.info(`📡 Checking for app updates`);
|
||||
const updater = new AppUpdater();
|
||||
|
||||
updater.start();
|
||||
|
||||
registerFileProtocol("static", __static);
|
||||
|
||||
await installDeveloperTools();
|
||||
@@ -133,6 +128,7 @@ app.on("ready", async () => {
|
||||
|
||||
logger.info("🖥️ Starting WindowManager");
|
||||
windowManager = WindowManager.getInstance<WindowManager>(proxyPort);
|
||||
windowManager.whenLoaded.then(() => startUpdateChecking());
|
||||
|
||||
logger.info("🧩 Initializing extensions");
|
||||
|
||||
|
||||
+4
-12
@@ -1,9 +1,9 @@
|
||||
import path from "path";
|
||||
import packageInfo from "../../package.json";
|
||||
import { dialog, Menu, NativeImage, Tray } from "electron";
|
||||
import { Menu, NativeImage, Tray } from "electron";
|
||||
import { autorun } from "mobx";
|
||||
import { showAbout } from "./menu";
|
||||
import { AppUpdater } from "./app-updater";
|
||||
import { checkForUpdates } from "./app-updater";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { clusterStore } from "../common/cluster-store";
|
||||
import { workspaceStore } from "../common/workspace-store";
|
||||
@@ -102,16 +102,8 @@ function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
{
|
||||
label: "Check for updates",
|
||||
async click() {
|
||||
const result = await AppUpdater.checkForUpdates();
|
||||
|
||||
if (!result) {
|
||||
const browserWindow = await windowManager.ensureMainWindow();
|
||||
|
||||
dialog.showMessageBoxSync(browserWindow, {
|
||||
message: "No updates available",
|
||||
type: "info",
|
||||
});
|
||||
}
|
||||
await checkForUpdates();
|
||||
await windowManager.ensureMainWindow();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ClusterId } from "../common/cluster-store";
|
||||
import { observable } from "mobx";
|
||||
import { observable, when } from "mobx";
|
||||
import { app, BrowserWindow, dialog, shell, webContents } from "electron";
|
||||
import windowStateKeeper from "electron-window-state";
|
||||
import { appEventBus } from "../common/event-bus";
|
||||
@@ -16,6 +16,9 @@ export class WindowManager extends Singleton {
|
||||
protected windowState: windowStateKeeper.State;
|
||||
protected disposers: Record<string, Function> = {};
|
||||
|
||||
@observable mainViewInitiallyLoaded = false;
|
||||
whenLoaded = when(() => this.mainViewInitiallyLoaded);
|
||||
|
||||
@observable activeClusterId: ClusterId;
|
||||
|
||||
constructor(protected proxyPort: number) {
|
||||
@@ -101,6 +104,7 @@ export class WindowManager extends Singleton {
|
||||
setTimeout(() => {
|
||||
appEventBus.emit({ name: "app", action: "start" });
|
||||
}, 1000);
|
||||
this.mainViewInitiallyLoaded = true;
|
||||
} catch (err) {
|
||||
dialog.showErrorBox("ERROR!", err.toString());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user