mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Add auto updater configuration (#122)
Adds a new set of configurations for managing whether the app will automatically check for updates. Ports over the auto update code from the old app. In this version, the main difference is that updates can be manually checked for using a menu bar item, even if auto updates are disabled.
This commit is contained in:
parent
776ccd7da0
commit
c47e17903d
@ -54,6 +54,8 @@ tasks:
|
||||
generates:
|
||||
- frontend/types/gotypes.d.ts
|
||||
- pkg/wshrpc/wshclient/wshclient.go
|
||||
- frontend/app/store/services.ts
|
||||
- frontend/app/store/wshserver.ts
|
||||
|
||||
build:server:
|
||||
desc: Build the wavesrv component.
|
||||
|
@ -73,7 +73,7 @@ const config = {
|
||||
},
|
||||
publish: {
|
||||
provider: "generic",
|
||||
url: "https://dl.waveterm.dev/releases",
|
||||
url: "https://dl.waveterm.dev/releases-w2",
|
||||
},
|
||||
};
|
||||
|
||||
|
227
emain/emain.ts
227
emain/emain.ts
@ -3,6 +3,7 @@
|
||||
|
||||
import { WaveDevVarName, WaveDevViteVarName } from "@/util/isdev";
|
||||
import * as electron from "electron";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import fs from "fs";
|
||||
import * as child_process from "node:child_process";
|
||||
import os from "os";
|
||||
@ -536,21 +537,60 @@ function convertMenuDefArrToMenu(menuDefArr: ElectronContextMenuItem[]): electro
|
||||
}
|
||||
|
||||
function makeAppMenu() {
|
||||
const fileMenu: Electron.MenuItemConstructorOptions[] = [];
|
||||
fileMenu.push({
|
||||
label: "New Window",
|
||||
accelerator: "CommandOrControl+N",
|
||||
click: () => fireAndForget(createNewWaveWindow),
|
||||
});
|
||||
fileMenu.push({
|
||||
role: "close",
|
||||
click: () => {
|
||||
electron.BrowserWindow.getFocusedWindow()?.close();
|
||||
const fileMenu: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: "New Window",
|
||||
accelerator: "CommandOrControl+N",
|
||||
click: () => fireAndForget(createNewWaveWindow),
|
||||
},
|
||||
});
|
||||
{
|
||||
role: "close",
|
||||
click: () => {
|
||||
electron.BrowserWindow.getFocusedWindow()?.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
const appMenu: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
role: "about",
|
||||
},
|
||||
{
|
||||
label: "Check for Updates",
|
||||
click: () => {
|
||||
const checkingNotification = new electron.Notification({
|
||||
title: "Wave Terminal",
|
||||
body: "Checking for updates.",
|
||||
});
|
||||
checkingNotification.show();
|
||||
fireAndForget(() => checkForUpdates());
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
role: "services",
|
||||
},
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
role: "hide",
|
||||
},
|
||||
{
|
||||
role: "hideOthers",
|
||||
},
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
role: "quit",
|
||||
},
|
||||
];
|
||||
const menuTemplate: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
role: "appMenu",
|
||||
submenu: appMenu,
|
||||
},
|
||||
{
|
||||
role: "fileMenu",
|
||||
@ -596,6 +636,170 @@ process.on("uncaughtException", (error) => {
|
||||
electron.app.quit();
|
||||
});
|
||||
|
||||
// ====== AUTO-UPDATER ====== //
|
||||
let autoUpdateLock = false;
|
||||
let autoUpdateInterval: NodeJS.Timeout | null = null;
|
||||
let availableUpdateReleaseName: string | null = null;
|
||||
let availableUpdateReleaseNotes: string | null = null;
|
||||
let appUpdateStatus = "unavailable";
|
||||
let lastUpdateCheck: Date = null;
|
||||
|
||||
/**
|
||||
* Sets the app update status and sends it to the main window
|
||||
* @param status The AppUpdateStatus to set, either "ready" or "unavailable"
|
||||
*/
|
||||
function setAppUpdateStatus(status: string) {
|
||||
appUpdateStatus = status;
|
||||
electron.BrowserWindow.getAllWindows().forEach((window) => {
|
||||
window.webContents.send("app-update-status", appUpdateStatus);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an hour has passed since the last update check, and if so, checks for updates using the `autoUpdater` object
|
||||
*/
|
||||
async function checkForUpdates() {
|
||||
const autoUpdateOpts = (await services.FileService.GetSettingsConfig()).autoupdate;
|
||||
|
||||
if (!autoUpdateOpts.enabled) {
|
||||
console.log("Auto update is disabled in settings. Removing the auto update interval.");
|
||||
clearInterval(autoUpdateInterval);
|
||||
autoUpdateInterval = null;
|
||||
return;
|
||||
}
|
||||
const now = new Date();
|
||||
if (!lastUpdateCheck || Math.abs(now.getTime() - lastUpdateCheck.getTime()) > autoUpdateOpts.intervalms) {
|
||||
fireAndForget(() => autoUpdater.checkForUpdates());
|
||||
lastUpdateCheck = now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the updater and sets up event listeners
|
||||
*/
|
||||
function initUpdater() {
|
||||
if (isDev) {
|
||||
console.log("skipping auto-updater in dev mode");
|
||||
return null;
|
||||
}
|
||||
|
||||
setAppUpdateStatus("unavailable");
|
||||
|
||||
autoUpdater.removeAllListeners();
|
||||
|
||||
autoUpdater.on("error", (err) => {
|
||||
console.log("updater error");
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
autoUpdater.on("checking-for-update", () => {
|
||||
console.log("checking-for-update");
|
||||
});
|
||||
|
||||
autoUpdater.on("update-available", () => {
|
||||
console.log("update-available; downloading...");
|
||||
});
|
||||
|
||||
autoUpdater.on("update-not-available", () => {
|
||||
console.log("update-not-available");
|
||||
});
|
||||
|
||||
autoUpdater.on("update-downloaded", (event) => {
|
||||
console.log("update-downloaded", [event]);
|
||||
availableUpdateReleaseName = event.releaseName;
|
||||
availableUpdateReleaseNotes = event.releaseNotes as string | null;
|
||||
|
||||
// Display the update banner and create a system notification
|
||||
setAppUpdateStatus("ready");
|
||||
const updateNotification = new electron.Notification({
|
||||
title: "Wave Terminal",
|
||||
body: "A new version of Wave Terminal is ready to install.",
|
||||
});
|
||||
updateNotification.on("click", () => {
|
||||
fireAndForget(() => installAppUpdate());
|
||||
});
|
||||
updateNotification.show();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the auto update check interval.
|
||||
* @returns The timeout object for the auto update checker.
|
||||
*/
|
||||
function startAutoUpdateInterval(): NodeJS.Timeout {
|
||||
// check for updates right away and keep checking later
|
||||
checkForUpdates();
|
||||
return setInterval(() => {
|
||||
checkForUpdates();
|
||||
}, 600000); // intervals are unreliable when an app is suspended so we will check every 10 mins if an hour has passed.
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to install the downloaded application update and restarts the application
|
||||
*/
|
||||
async function installAppUpdate() {
|
||||
const dialogOpts: Electron.MessageBoxOptions = {
|
||||
type: "info",
|
||||
buttons: ["Restart", "Later"],
|
||||
title: "Application Update",
|
||||
message: process.platform === "win32" ? availableUpdateReleaseNotes : availableUpdateReleaseName,
|
||||
detail: "A new version has been downloaded. Restart the application to apply the updates.",
|
||||
};
|
||||
|
||||
const allWindows = electron.BrowserWindow.getAllWindows();
|
||||
if (allWindows.length > 0) {
|
||||
await electron.dialog
|
||||
.showMessageBox(electron.BrowserWindow.getFocusedWindow() ?? allWindows[0], dialogOpts)
|
||||
.then(({ response }) => {
|
||||
if (response === 0) autoUpdater.quitAndInstall();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
electron.ipcMain.on("install-app-update", () => fireAndForget(() => installAppUpdate()));
|
||||
electron.ipcMain.on("get-app-update-status", (event) => {
|
||||
event.returnValue = appUpdateStatus;
|
||||
});
|
||||
|
||||
/**
|
||||
* Configures the auto-updater based on the user's preference
|
||||
* @param enabled Whether the auto-updater should be enabled
|
||||
*/
|
||||
async function configureAutoUpdater() {
|
||||
// simple lock to prevent multiple auto-update configuration attempts, this should be very rare
|
||||
if (autoUpdateLock) {
|
||||
console.log("auto-update configuration already in progress, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
autoUpdateLock = true;
|
||||
|
||||
const autoUpdateEnabled = (await services.FileService.GetSettingsConfig()).autoupdate.enabled;
|
||||
|
||||
try {
|
||||
console.log("Configuring updater");
|
||||
initUpdater();
|
||||
} catch (e) {
|
||||
console.warn("error configuring updater", e.toString());
|
||||
}
|
||||
|
||||
if (autoUpdateEnabled && autoUpdateInterval == null) {
|
||||
lastUpdateCheck = null;
|
||||
try {
|
||||
console.log("configuring auto update interval");
|
||||
autoUpdateInterval = startAutoUpdateInterval();
|
||||
} catch (e) {
|
||||
console.log("error configuring auto update interval", e.toString());
|
||||
}
|
||||
} else if (!autoUpdateEnabled && autoUpdateInterval != null) {
|
||||
console.log("disabling auto updater");
|
||||
clearInterval(autoUpdateInterval);
|
||||
autoUpdateInterval = null;
|
||||
}
|
||||
autoUpdateLock = false;
|
||||
}
|
||||
// ====== AUTO-UPDATER ====== //
|
||||
|
||||
async function appMain() {
|
||||
const startTs = Date.now();
|
||||
const instanceLock = electronApp.requestSingleInstanceLock();
|
||||
@ -637,6 +841,7 @@ async function appMain() {
|
||||
console.log("show", win.waveWindowId);
|
||||
win.show();
|
||||
}
|
||||
configureAutoUpdater();
|
||||
globalIsStarting = false;
|
||||
|
||||
electronApp.on("activate", () => {
|
||||
|
@ -49,7 +49,7 @@ class FileServiceType {
|
||||
DeleteFile(arg1: string): Promise<void> {
|
||||
return WOS.callBackendService("file", "DeleteFile", Array.from(arguments))
|
||||
}
|
||||
GetSettingsConfig(): Promise<any> {
|
||||
GetSettingsConfig(): Promise<SettingsConfigType> {
|
||||
return WOS.callBackendService("file", "GetSettingsConfig", Array.from(arguments))
|
||||
}
|
||||
GetWaveFile(arg1: string, arg2: string): Promise<any> {
|
||||
|
7
frontend/types/gotypes.d.ts
vendored
7
frontend/types/gotypes.d.ts
vendored
@ -5,6 +5,12 @@
|
||||
|
||||
declare global {
|
||||
|
||||
// wconfig.AutoUpdateOpts
|
||||
type AutoUpdateOpts = {
|
||||
enabled: boolean;
|
||||
intervalms: number;
|
||||
};
|
||||
|
||||
// wstore.Block
|
||||
type Block = WaveObj & {
|
||||
blockdef: BlockDef;
|
||||
@ -214,6 +220,7 @@ declare global {
|
||||
term: TerminalConfigType;
|
||||
widgets: WidgetsConfigType[];
|
||||
blockheader: BlockHeaderOpts;
|
||||
autoupdate: AutoUpdateOpts;
|
||||
};
|
||||
|
||||
// wstore.StickerClickOptsType
|
||||
|
@ -82,6 +82,7 @@
|
||||
"base64-js": "^1.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.11",
|
||||
"electron-updater": "6.2.1",
|
||||
"html-to-image": "^1.11.11",
|
||||
"immer": "^10.1.1",
|
||||
"jotai": "^2.8.0",
|
||||
|
@ -161,7 +161,7 @@ func (fs *FileService) DeleteFile(path string) error {
|
||||
return os.Remove(cleanedPath)
|
||||
}
|
||||
|
||||
func (fs *FileService) GetSettingsConfig() interface{} {
|
||||
func (fs *FileService) GetSettingsConfig() wconfig.SettingsConfigType {
|
||||
watcher := wconfig.GetWatcher()
|
||||
return watcher.GetSettingsConfig()
|
||||
}
|
||||
|
@ -36,11 +36,17 @@ type BlockHeaderOpts struct {
|
||||
ShowBlockIds bool `json:"showblockids"`
|
||||
}
|
||||
|
||||
type AutoUpdateOpts struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
IntervalMs uint32 `json:"intervalms"`
|
||||
}
|
||||
|
||||
type SettingsConfigType struct {
|
||||
MimeTypes map[string]MimeTypeConfigType `json:"mimetypes"`
|
||||
Term TerminalConfigType `json:"term"`
|
||||
Widgets []WidgetsConfigType `json:"widgets"`
|
||||
BlockHeader BlockHeaderOpts `json:"blockheader"`
|
||||
AutoUpdate AutoUpdateOpts `json:"autoupdate"`
|
||||
}
|
||||
|
||||
func getSettingsConfigDefaults() SettingsConfigType {
|
||||
@ -97,5 +103,9 @@ func getSettingsConfigDefaults() SettingsConfigType {
|
||||
},
|
||||
},
|
||||
},
|
||||
AutoUpdate: AutoUpdateOpts{
|
||||
Enabled: true,
|
||||
IntervalMs: 3600000,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
38
yarn.lock
38
yarn.lock
@ -7154,6 +7154,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-updater@npm:6.2.1":
|
||||
version: 6.2.1
|
||||
resolution: "electron-updater@npm:6.2.1"
|
||||
dependencies:
|
||||
builder-util-runtime: "npm:9.2.4"
|
||||
fs-extra: "npm:^10.1.0"
|
||||
js-yaml: "npm:^4.1.0"
|
||||
lazy-val: "npm:^1.0.5"
|
||||
lodash.escaperegexp: "npm:^4.1.2"
|
||||
lodash.isequal: "npm:^4.5.0"
|
||||
semver: "npm:^7.3.8"
|
||||
tiny-typed-emitter: "npm:^2.1.0"
|
||||
checksum: 10c0/b376e13bf2b4675ca853c4164a4caf9454d0d41e797c0f8fd011d66693d4eb5ece020953f3d06c0a63d9d5077a9ae9e8447f26c602da317edb1521c4bd99e2f8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-vite@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "electron-vite@npm:2.2.0"
|
||||
@ -9970,6 +9986,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.escaperegexp@npm:^4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "lodash.escaperegexp@npm:4.1.2"
|
||||
checksum: 10c0/484ad4067fa9119bb0f7c19a36ab143d0173a081314993fe977bd00cf2a3c6a487ce417a10f6bac598d968364f992153315f0dbe25c9e38e3eb7581dd333e087
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.isequal@npm:^4.5.0":
|
||||
version: 4.5.0
|
||||
resolution: "lodash.isequal@npm:4.5.0"
|
||||
checksum: 10c0/dfdb2356db19631a4b445d5f37868a095e2402292d59539a987f134a8778c62a2810c2452d11ae9e6dcac71fc9de40a6fedcb20e2952a15b431ad8b29e50e28f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.merge@npm:^4.6.2":
|
||||
version: 4.6.2
|
||||
resolution: "lodash.merge@npm:4.6.2"
|
||||
@ -13504,6 +13534,7 @@ __metadata:
|
||||
dayjs: "npm:^1.11.11"
|
||||
electron: "npm:^31.1.0"
|
||||
electron-builder: "npm:^24.13.3"
|
||||
electron-updater: "npm:6.2.1"
|
||||
electron-vite: "npm:^2.2.0"
|
||||
eslint: "npm:^9.2.0"
|
||||
eslint-config-prettier: "npm:^9.1.0"
|
||||
@ -13573,6 +13604,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tiny-typed-emitter@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "tiny-typed-emitter@npm:2.1.0"
|
||||
checksum: 10c0/522bed4c579ee7ee16548540cb693a3d098b137496110f5a74bff970b54187e6b7343a359b703e33f77c5b4b90ec6cebc0d0ec3dbdf1bd418723c5c3ce36d8a2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinybench@npm:^2.5.1":
|
||||
version: 2.8.0
|
||||
resolution: "tinybench@npm:2.8.0"
|
||||
|
Loading…
Reference in New Issue
Block a user